diff --git a/shiny/ui/_download_button.py b/shiny/ui/_download_button.py index 52ab48d1c..1ef80c240 100644 --- a/shiny/ui/_download_button.py +++ b/shiny/ui/_download_button.py @@ -48,18 +48,19 @@ def download_button( return tags.a( icon, label, - {"class": "btn btn-default shiny-download-link", "style": css(width=width)}, + { + "class": "btn btn-default shiny-download-link disabled", + "style": css(width=width), + }, id=resolve_id(id), - # This is a fake link that just results in a 404. It will be replaced by a - # working link after the server side logic runs, so this link will only be - # visited in cases where the user clicks the button too fast, or if the server - # never defines a handler for this download button. - href="session/0/download/missing_download", + href="", target="_blank", # We can't use `download` in pyodide mode, because the browser chooses not to # route the download through the service worker in that case. (Observed by # jcheng on 1/7/2022, using Chrome Version 96.0.4664.110.) download=None if is_pyodide else True, + aria_disabled="true", + tabindex="-1", **kwargs, ) @@ -103,10 +104,12 @@ def download_link( return tags.a( icon, label, - {"class": "shiny-download-link", "style": css(width=width)}, + {"class": "shiny-download-link disabled", "style": css(width=width)}, id=resolve_id(id), - href="session/0/download/missing_download", + href="", target="_blank", download=None if is_pyodide else True, + aria_disabled="true", + tabindex="-1", **kwargs, ) diff --git a/shiny/www/shared/_version.json b/shiny/www/shared/_version.json index 0db4b2569..9512036f7 100644 --- a/shiny/www/shared/_version.json +++ b/shiny/www/shared/_version.json @@ -1,5 +1,5 @@ { "note!": "This file is auto-generated by scripts/htmlDependencies.R", "package": "shiny", - "version": "Github (rstudio/shiny@950c63049b37cb3156bf134fa4ff13ad3cedee48)" + "version": "Github (rstudio/shiny@2872c87e3239ffd4f8ab8560974a98600dcd755d)" } diff --git a/shiny/www/shared/bootstrap/_version.json b/shiny/www/shared/bootstrap/_version.json index 3a4f0d1de..cacc16046 100644 --- a/shiny/www/shared/bootstrap/_version.json +++ b/shiny/www/shared/bootstrap/_version.json @@ -1,7 +1,7 @@ { "note!": "This file is auto-generated by scripts/htmlDependencies.R", - "shiny_version": "Github (rstudio/shiny@950c63049b37cb3156bf134fa4ff13ad3cedee48)", - "bslib_version": "Github (rstudio/bslib@2c87d0c8753d003fef66858235ead8c9206aefa9)", + "shiny_version": "Github (rstudio/shiny@2872c87e3239ffd4f8ab8560974a98600dcd755d)", + "bslib_version": "Github (rstudio/bslib@3dd55fd7249d30ecddac8f98ba8dc70aee0b3459)", "htmltools_version": "Github (rstudio/htmltools@038ef7be3b02a9248f122b745ad7830cc429d437)", "bootstrap_version": "5.3.1" } diff --git a/shiny/www/shared/bslib/_version.json b/shiny/www/shared/bslib/_version.json index ceea1f7ba..55d7eca0c 100644 --- a/shiny/www/shared/bslib/_version.json +++ b/shiny/www/shared/bslib/_version.json @@ -1,5 +1,5 @@ { "note!": "This file is auto-generated by scripts/htmlDependencies.R", "package": "bslib", - "version": "Github (rstudio/bslib@2c87d0c8753d003fef66858235ead8c9206aefa9)" + "version": "Github (rstudio/bslib@3dd55fd7249d30ecddac8f98ba8dc70aee0b3459)" } diff --git a/shiny/www/shared/bslib/components/components.min.js b/shiny/www/shared/bslib/components/components.min.js index da59feab2..1f0ba3b01 100644 --- a/shiny/www/shared/bslib/components/components.min.js +++ b/shiny/www/shared/bslib/components/components.min.js @@ -1,5 +1,5 @@ /*! bslib 0.7.0.9000 | (c) 2012-2024 RStudio, PBC. | License: MIT + file LICENSE */ -"use strict";(()=>{var f=(r,e)=>()=>(r&&(e=r(r=0)),e);var X=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports);var x=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var v=(r,e,t)=>(x(r,e,"read from private field"),t?t.call(r):e.get(r)),H=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)};var A=(r,e,t)=>(x(r,e,"access private method"),t);var h=(r,e,t)=>new Promise((n,i)=>{var s=o=>{try{d(t.next(o))}catch(b){i(b)}},l=o=>{try{d(t.throw(o))}catch(b){i(b)}},d=o=>o.done?n(o.value):Promise.resolve(o.value).then(s,l);d((t=t.apply(r,e)).next())});function y(r,e){u&&u.inputBindings.register(new r,"bslib."+e)}function w(r,e){return Object.prototype.hasOwnProperty.call(r,e)&&r[e]!==void 0}function k(r){let e=["a[href]","area[href]","button","details summary","input","iframe","select","textarea",'[contentEditable=""]','[contentEditable="true"]','[contentEditable="TRUE"]',"[tabindex]"],t=[':not([tabindex="-1"])',":not([disabled])"],n=e.map(s=>s+t.join("")),i=r.querySelectorAll(n.join(", "));return Array.from(i)}function E(...r){return h(this,null,function*(){if(!u)throw new Error("This function must be called in a Shiny app.");return u.renderContentAsync?yield u.renderContentAsync.apply(null,r):yield u.renderContent.apply(null,r)})}var u,m,L=f(()=>{"use strict";u=window.Shiny,m=u?u.InputBinding:class{}});var O,U=f(()=>{"use strict";L();O=class extends m{find(e){return $(e).find(".accordion.bslib-accordion-input")}getValue(e){let n=this._getItemInfo(e).filter(i=>i.isOpen()).map(i=>i.value);return n.length===0?null:n}subscribe(e,t){$(e).on("shown.bs.collapse.accordionInputBinding hidden.bs.collapse.accordionInputBinding",function(n){t(!0)})}unsubscribe(e){$(e).off(".accordionInputBinding")}receiveMessage(e,t){return h(this,null,function*(){let n=t.method;if(n==="set")this._setItems(e,t);else if(n==="open")this._openItems(e,t);else if(n==="close")this._closeItems(e,t);else if(n==="remove")this._removeItem(e,t);else if(n==="insert")yield this._insertItem(e,t);else if(n==="update")yield this._updateItem(e,t);else throw new Error(`Method not yet implemented: ${n}`)})}_setItems(e,t){let n=this._getItemInfo(e),i=this._getValues(e,n,t.values);n.forEach(s=>{i.indexOf(s.value)>-1?s.show():s.hide()})}_openItems(e,t){let n=this._getItemInfo(e),i=this._getValues(e,n,t.values);n.forEach(s=>{i.indexOf(s.value)>-1&&s.show()})}_closeItems(e,t){let n=this._getItemInfo(e),i=this._getValues(e,n,t.values);n.forEach(s=>{i.indexOf(s.value)>-1&&s.hide()})}_insertItem(e,t){return h(this,null,function*(){let n=this._findItem(e,t.target);n||(n=t.position==="before"?e.firstElementChild:e.lastElementChild);let i=t.panel;if(n?yield E(n,i,t.position==="before"?"beforeBegin":"afterEnd"):yield E(e,i),this._isAutoClosing(e)){let s=$(i.html).attr("data-value");$(e).find(`[data-value="${s}"] .accordion-collapse`).attr("data-bs-parent","#"+e.id)}})}_removeItem(e,t){let n=this._getItemInfo(e).filter(s=>t.target.indexOf(s.value)>-1),i=Shiny==null?void 0:Shiny.unbindAll;n.forEach(s=>{i&&i(s.item),s.item.remove()})}_updateItem(e,t){return h(this,null,function*(){let n=this._findItem(e,t.target);if(!n)throw new Error(`Unable to find an accordion_panel() with a value of ${t.target}`);if(w(t,"value")&&(n.dataset.value=t.value),w(t,"body")){let s=n.querySelector(".accordion-body");yield E(s,t.body)}let i=n.querySelector(".accordion-header");if(w(t,"title")){let s=i.querySelector(".accordion-title");yield E(s,t.title)}if(w(t,"icon")){let s=i.querySelector(".accordion-button > .accordion-icon");yield E(s,t.icon)}})}_getItemInfo(e){return Array.from(e.querySelectorAll(":scope > .accordion-item")).map(n=>this._getSingleItemInfo(n))}_getSingleItemInfo(e){let t=e.querySelector(".accordion-collapse"),n=()=>$(t).hasClass("show");return{item:e,value:e.dataset.value,isOpen:n,show:()=>{n()||$(t).collapse("show")},hide:()=>{n()&&$(t).collapse("hide")}}}_getValues(e,t,n){let i=n!==!0?n:t.map(l=>l.value);return this._isAutoClosing(e)&&(i=i.slice(i.length-1,i.length)),i}_findItem(e,t){return e.querySelector(`[data-value="${t}"]`)}_isAutoClosing(e){return e.classList.contains("autoclose")}};y(O,"accordion")});var S,R=f(()=>{"use strict";S=class{constructor(){this.resizeObserverEntries=[],this.resizeObserver=new ResizeObserver(e=>{let t=new Event("resize");if(window.dispatchEvent(t),!window.Shiny)return;let n=[];for(let i of e)i.target instanceof HTMLElement&&i.target.querySelector(".shiny-bound-output")&&i.target.querySelectorAll(".shiny-bound-output").forEach(s=>{if(n.includes(s))return;let{binding:l,onResize:d}=$(s).data("shinyOutputBinding");if(!l||!l.resize)return;let o=s.shinyResizeObserver;if(o&&o!==this||(o||(s.shinyResizeObserver=this),d(s),n.push(s),!s.classList.contains("shiny-plot-output")))return;let b=s.querySelector('img:not([width="100%"])');b&&b.setAttribute("width","100%")})})}observe(e){this.resizeObserver.observe(e),this.resizeObserverEntries.push(e)}unobserve(e){let t=this.resizeObserverEntries.indexOf(e);t<0||(this.resizeObserver.unobserve(e),this.resizeObserverEntries.splice(t,1))}flush(){this.resizeObserverEntries.forEach(e=>{document.body.contains(e)||this.unobserve(e)})}}});var _,q=f(()=>{"use strict";_=class{constructor(e,t){this.watching=new Set,this.observer=new MutationObserver(n=>{let i=new Set;for(let{type:s,removedNodes:l}of n)if(s==="childList"&&l.length!==0)for(let d of l)d instanceof HTMLElement&&(d.matches(e)&&i.add(d),d.querySelector(e)&&d.querySelectorAll(e).forEach(o=>i.add(o)));if(i.size!==0)for(let s of i)try{t(s)}catch(l){console.error(l)}})}observe(e){let t=this._flush();if(this.watching.has(e)){if(!t)return}else this.watching.add(e);t?this._restartObserver():this.observer.observe(e,{childList:!0,subtree:!0})}unobserve(e){this.watching.has(e)&&(this.watching.delete(e),this._flush(),this._restartObserver())}_restartObserver(){this.observer.disconnect();for(let e of this.watching)this.observer.observe(e,{childList:!0,subtree:!0})}_flush(){let e=!1,t=Array.from(this.watching);for(let n of t)document.body.contains(n)||(this.watching.delete(n),e=!0);return e}}});var a,g,B=f(()=>{"use strict";L();R();q();a=class{constructor(e){var t;e.removeAttribute(a.attr.ATTR_INIT),(t=e.querySelector(`script[${a.attr.ATTR_INIT}]`))==null||t.remove(),this.card=e,a.instanceMap.set(e,this),a.shinyResizeObserver.observe(this.card),a.cardRemovedObserver.observe(document.body),this._addEventListeners(),this.overlay=this._createOverlay(),this._setShinyInput(),this._exitFullScreenOnEscape=this._exitFullScreenOnEscape.bind(this),this._trapFocusExit=this._trapFocusExit.bind(this)}enterFullScreen(e){var t;e&&e.preventDefault(),this.card.id&&this.overlay.anchor.setAttribute("aria-controls",this.card.id),document.addEventListener("keydown",this._exitFullScreenOnEscape,!1),document.addEventListener("keydown",this._trapFocusExit,!0),this.card.setAttribute(a.attr.ATTR_FULL_SCREEN,"true"),document.body.classList.add(a.attr.CLASS_HAS_FULL_SCREEN),this.card.insertAdjacentElement("beforebegin",this.overlay.container),(!this.card.contains(document.activeElement)||(t=document.activeElement)!=null&&t.classList.contains(a.attr.CLASS_FULL_SCREEN_ENTER))&&(this.card.setAttribute("tabindex","-1"),this.card.focus()),this._emitFullScreenEvent(!0),this._setShinyInput()}exitFullScreen(){document.removeEventListener("keydown",this._exitFullScreenOnEscape,!1),document.removeEventListener("keydown",this._trapFocusExit,!0),this.overlay.container.remove(),this.card.setAttribute(a.attr.ATTR_FULL_SCREEN,"false"),this.card.removeAttribute("tabindex"),document.body.classList.remove(a.attr.CLASS_HAS_FULL_SCREEN),this._emitFullScreenEvent(!1),this._setShinyInput()}_setShinyInput(){if(!this.card.classList.contains(a.attr.CLASS_SHINY_INPUT)||!u)return;if(!u.setInputValue){setTimeout(()=>this._setShinyInput(),0);return}let e=this.card.getAttribute(a.attr.ATTR_FULL_SCREEN);u.setInputValue(this.card.id+"_full_screen",e==="true")}_emitFullScreenEvent(e){let t=new CustomEvent("bslib.card",{bubbles:!0,detail:{fullScreen:e}});this.card.dispatchEvent(t)}_addEventListeners(){let e=this.card.querySelector(`:scope > * > .${a.attr.CLASS_FULL_SCREEN_ENTER}`);e&&e.addEventListener("click",t=>this.enterFullScreen(t))}_exitFullScreenOnEscape(e){if(!(e.target instanceof HTMLElement))return;let t=["select[open]","input[aria-expanded='true']"];e.target.matches(t.join(", "))||e.key==="Escape"&&this.exitFullScreen()}_trapFocusExit(e){if(!(e instanceof KeyboardEvent)||e.key!=="Tab")return;let t=e.target===this.card,n=e.target===this.overlay.anchor,i=this.card.contains(e.target),s=()=>{e.preventDefault(),e.stopImmediatePropagation()};if(!(i||t||n)){s(),this.card.focus();return}let l=k(this.card).filter(I=>!I.classList.contains(a.attr.CLASS_FULL_SCREEN_ENTER));if(!(l.length>0)){s(),this.overlay.anchor.focus();return}if(t)return;let o=l[l.length-1],b=e.target===o;if(n&&e.shiftKey){s(),o.focus();return}if(b&&!e.shiftKey){s(),this.overlay.anchor.focus();return}}_createOverlay(){let e=document.createElement("div");e.id=a.attr.ID_FULL_SCREEN_OVERLAY,e.onclick=this.exitFullScreen.bind(this);let t=this._createOverlayCloseAnchor();return e.appendChild(t),{container:e,anchor:t}}_createOverlayCloseAnchor(){let e=document.createElement("a");return e.classList.add(a.attr.CLASS_FULL_SCREEN_EXIT),e.tabIndex=0,e.setAttribute("aria-expanded","true"),e.setAttribute("aria-label","Close card"),e.setAttribute("role","button"),e.onclick=t=>{this.exitFullScreen(),t.stopPropagation()},e.onkeydown=t=>{(t.key==="Enter"||t.key===" ")&&this.exitFullScreen()},e.innerHTML=this._overlayCloseHtml(),e}_overlayCloseHtml(){return"Close "}static getInstance(e){return a.instanceMap.get(e)}static initializeAllCards(e=!0){if(document.readyState==="loading"){a.onReadyScheduled||(a.onReadyScheduled=!0,document.addEventListener("DOMContentLoaded",()=>{a.initializeAllCards(!1)}));return}e&&a.shinyResizeObserver.flush();let t=`.${a.attr.CLASS_CARD}[${a.attr.ATTR_INIT}]`;if(!document.querySelector(t))return;document.querySelectorAll(t).forEach(i=>new a(i))}},g=a;g.attr={ATTR_INIT:"data-bslib-card-init",CLASS_CARD:"bslib-card",ATTR_FULL_SCREEN:"data-full-screen",CLASS_HAS_FULL_SCREEN:"bslib-has-full-screen",CLASS_FULL_SCREEN_ENTER:"bslib-full-screen-enter",CLASS_FULL_SCREEN_EXIT:"bslib-full-screen-exit",ID_FULL_SCREEN_OVERLAY:"bslib-full-screen-overlay",CLASS_SHINY_INPUT:"bslib-card-input"},g.shinyResizeObserver=new S,g.cardRemovedObserver=new _(`.${a.attr.CLASS_CARD}`,e=>{let t=a.getInstance(e);t&&t.card.getAttribute(a.attr.ATTR_FULL_SCREEN)==="true"&&t.exitFullScreen()}),g.instanceMap=new WeakMap,g.onReadyScheduled=!1;window.bslib=window.bslib||{};window.bslib.Card=g});var c,p,z,D=f(()=>{"use strict";L();R();c=class{constructor(e){this.windowSize="";var i;c.instanceMap.set(e,this),this.layout={container:e,main:e.querySelector(":scope > .main"),sidebar:e.querySelector(":scope > .sidebar"),toggle:e.querySelector(":scope > .collapse-toggle")};let t=this.layout.sidebar.querySelector(":scope > .sidebar-content > .accordion");t&&((i=t==null?void 0:t.parentElement)==null||i.classList.add("has-accordion"),t.classList.add("accordion-flush")),this._initSidebarCounters(),this._initSidebarState(),(this._isCollapsible("desktop")||this._isCollapsible("mobile"))&&this._initEventListeners(),c.shinyResizeObserver.observe(this.layout.main),e.removeAttribute("data-bslib-sidebar-init");let n=e.querySelector(":scope > script[data-bslib-sidebar-init]");n&&e.removeChild(n)}get isClosed(){return this.layout.container.classList.contains(c.classes.COLLAPSE)}static getInstance(e){return c.instanceMap.get(e)}_isCollapsible(e="desktop"){let{container:t}=this.layout,n=e==="desktop"?"collapsibleDesktop":"collapsibleMobile",i=t.dataset[n];return i===void 0?!0:i.trim().toLowerCase()!=="false"}static initCollapsibleAll(e=!0){if(document.readyState==="loading"){c.onReadyScheduled||(c.onReadyScheduled=!0,document.addEventListener("DOMContentLoaded",()=>{c.initCollapsibleAll(!1)}));return}let t=`.${c.classes.LAYOUT}[data-bslib-sidebar-init]`;if(!document.querySelector(t))return;e&&c.shinyResizeObserver.flush(),document.querySelectorAll(t).forEach(i=>new c(i))}_initEventListeners(){var t;let{toggle:e}=this.layout;e.addEventListener("click",n=>{n.preventDefault(),this.toggle("toggle")}),(t=e.querySelector(".collapse-icon"))==null||t.addEventListener("transitionend",()=>this._finalizeState()),!(this._isCollapsible("desktop")&&this._isCollapsible("mobile"))&&window.addEventListener("resize",()=>this._handleWindowResizeEvent())}_initSidebarCounters(){let{container:e}=this.layout,t=`.${c.classes.LAYOUT}> .main > .${c.classes.LAYOUT}:not([data-bslib-sidebar-open="always"])`;if(!(e.querySelector(t)===null))return;function i(o){return o=o?o.parentElement:null,o&&o.classList.contains("main")&&(o=o.parentElement),o&&o.classList.contains(c.classes.LAYOUT)?o:null}let s=[e],l=i(e);for(;l;)s.unshift(l),l=i(l);let d={left:0,right:0};s.forEach(function(o){let I=o.classList.contains("sidebar-right")?d.right++:d.left++;o.style.setProperty("--_js-toggle-count-this-side",I.toString()),o.style.setProperty("--_js-toggle-count-max-side",Math.max(d.right,d.left).toString())})}_getWindowSize(){let{container:e}=this.layout;return window.getComputedStyle(e).getPropertyValue("--bslib-sidebar-js-window-size").trim()}_initialToggleState(){var i,s;let{container:e}=this.layout,t=this.windowSize==="desktop"?"openDesktop":"openMobile",n=(s=(i=e.dataset[t])==null?void 0:i.trim())==null?void 0:s.toLowerCase();return n===void 0||["open","always"].includes(n)?"open":["close","closed"].includes(n)?"close":"open"}_initSidebarState(){this.windowSize=this._getWindowSize();let e=this._initialToggleState();this.toggle(e,!0)}_handleWindowResizeEvent(){let e=this._getWindowSize();!e||e==this.windowSize||this._initSidebarState()}toggle(e,t=!1){typeof e=="undefined"&&(e="toggle");let{container:n,sidebar:i}=this.layout,s=this.isClosed;if(["open","close","toggle"].indexOf(e)===-1)throw new Error(`Unknown method ${e}`);if(e==="toggle"&&(e=s?"open":"close"),s&&e==="close"||!s&&e==="open"){t&&this._finalizeState();return}e==="open"&&(i.hidden=!1),n.classList.toggle(c.classes.TRANSITIONING,!t),n.classList.toggle(c.classes.COLLAPSE),t&&this._finalizeState()}_finalizeState(){let{container:e,sidebar:t,toggle:n}=this.layout;e.classList.remove(c.classes.TRANSITIONING),t.hidden=this.isClosed,n.setAttribute("aria-expanded",this.isClosed?"false":"true");let i=new CustomEvent("bslib.sidebar",{bubbles:!0,detail:{open:!this.isClosed}});t.dispatchEvent(i),$(t).trigger("toggleCollapse.sidebarInputBinding"),$(t).trigger(this.isClosed?"hidden":"shown")}},p=c;p.shinyResizeObserver=new S,p.classes={LAYOUT:"bslib-sidebar-layout",COLLAPSE:"sidebar-collapsed",TRANSITIONING:"transitioning"},p.onReadyScheduled=!1,p.instanceMap=new WeakMap;z=class extends m{find(e){return $(e).find(`.${p.classes.LAYOUT} > .bslib-sidebar-input`)}getValue(e){let t=p.getInstance(e.parentElement);return t?!t.isClosed:!1}setValue(e,t){let n=t?"open":"close";this.receiveMessage(e,{method:n})}subscribe(e,t){$(e).on("toggleCollapse.sidebarInputBinding",function(n){t(!0)})}unsubscribe(e){$(e).off(".sidebarInputBinding")}receiveMessage(e,t){let n=p.getInstance(e.parentElement);n&&n.toggle(t.method)}};y(z,"sidebar");window.bslib=window.bslib||{};window.bslib.Sidebar=p});var T,M,C,N,F,P=f(()=>{"use strict";L();F=class extends m{constructor(){super(...arguments);H(this,C);H(this,T,new WeakMap);H(this,M,new WeakMap)}find(t){return $(t).find(".bslib-task-button")}getValue(t){var n;return{value:(n=v(this,T).get(t))!=null?n:0,autoReset:t.hasAttribute("data-auto-reset")}}getType(){return"bslib.taskbutton"}subscribe(t,n){v(this,M).has(t)&&this.unsubscribe(t);let i=()=>{var s;v(this,T).set(t,((s=v(this,T).get(t))!=null?s:0)+1),n(!0),A(this,C,N).call(this,t,"busy")};v(this,M).set(t,i),t.addEventListener("click",i)}unsubscribe(t){let n=v(this,M).get(t);n&&t.removeEventListener("click",n)}receiveMessage(i,s){return h(this,arguments,function*(t,{state:n}){A(this,C,N).call(this,t,n)})}};T=new WeakMap,M=new WeakMap,C=new WeakSet,N=function(t,n){t.disabled=n==="busy";let i=t.querySelector("bslib-switch-inline");i&&(i.case=n)};y(F,"task-button")});function W(r){if(window.Shiny)for(let[e,t]of Object.entries(r))Shiny.addCustomMessageHandler(e,t)}var V=f(()=>{"use strict"});var K=X(j=>{U();B();D();P();L();V();var G={"bslib.toggle-input-binary":r=>h(j,null,function*(){let e=document.getElementById(r.id);e||console.warn("[bslib.toggle-input-binary] No element found",r);let t=$(e).data("shiny-input-binding");if(!(t instanceof m)){console.warn("[bslib.toggle-input-binary] No input binding found",r);return}let n=r.value;typeof n=="undefined"&&(n=!t.getValue(e)),yield t.receiveMessage(e,{value:n})})};window.Shiny&&W(G);function Y(){let r=document.createElement("div");r.innerHTML=` +"use strict";(()=>{var f=(r,e)=>()=>(r&&(e=r(r=0)),e);var X=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports);var k=(r,e,t)=>{if(!e.has(r))throw TypeError("Cannot "+t)};var v=(r,e,t)=>(k(r,e,"read from private field"),t?t.call(r):e.get(r)),H=(r,e,t)=>{if(e.has(r))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(r):e.set(r,t)};var O=(r,e,t)=>(k(r,e,"access private method"),t);var h=(r,e,t)=>new Promise((i,n)=>{var s=o=>{try{d(t.next(o))}catch(b){n(b)}},l=o=>{try{d(t.throw(o))}catch(b){n(b)}},d=o=>o.done?i(o.value):Promise.resolve(o.value).then(s,l);d((t=t.apply(r,e)).next())});function y(r,e){u&&u.inputBindings.register(new r,"bslib."+e)}function _(r,e){window.bslib=window.bslib||{},window.bslib[r]?console.error(`[bslib] Global window.bslib.${r} was already defined, using previous definition.`):window.bslib[r]=e}function w(r,e){return Object.prototype.hasOwnProperty.call(r,e)&&r[e]!==void 0}function U(r){let e=["a[href]","area[href]","button","details summary","input","iframe","select","textarea",'[contentEditable=""]','[contentEditable="true"]','[contentEditable="TRUE"]',"[tabindex]"],t=[':not([tabindex="-1"])',":not([disabled])"],i=e.map(s=>s+t.join("")),n=r.querySelectorAll(i.join(", "));return Array.from(n)}function E(...r){return h(this,null,function*(){if(!u)throw new Error("This function must be called in a Shiny app.");return u.renderContentAsync?yield u.renderContentAsync.apply(null,r):yield u.renderContent.apply(null,r)})}var u,m,L=f(()=>{"use strict";u=window.Shiny,m=u?u.InputBinding:class{}});var R,B=f(()=>{"use strict";L();R=class extends m{find(e){return $(e).find(".accordion.bslib-accordion-input")}getValue(e){let i=this._getItemInfo(e).filter(n=>n.isOpen()).map(n=>n.value);return i.length===0?null:i}subscribe(e,t){$(e).on("shown.bs.collapse.accordionInputBinding hidden.bs.collapse.accordionInputBinding",function(i){t(!0)})}unsubscribe(e){$(e).off(".accordionInputBinding")}receiveMessage(e,t){return h(this,null,function*(){let i=t.method;if(i==="set")this._setItems(e,t);else if(i==="open")this._openItems(e,t);else if(i==="close")this._closeItems(e,t);else if(i==="remove")this._removeItem(e,t);else if(i==="insert")yield this._insertItem(e,t);else if(i==="update")yield this._updateItem(e,t);else throw new Error(`Method not yet implemented: ${i}`)})}_setItems(e,t){let i=this._getItemInfo(e),n=this._getValues(e,i,t.values);i.forEach(s=>{n.indexOf(s.value)>-1?s.show():s.hide()})}_openItems(e,t){let i=this._getItemInfo(e),n=this._getValues(e,i,t.values);i.forEach(s=>{n.indexOf(s.value)>-1&&s.show()})}_closeItems(e,t){let i=this._getItemInfo(e),n=this._getValues(e,i,t.values);i.forEach(s=>{n.indexOf(s.value)>-1&&s.hide()})}_insertItem(e,t){return h(this,null,function*(){let i=this._findItem(e,t.target);i||(i=t.position==="before"?e.firstElementChild:e.lastElementChild);let n=t.panel;if(i?yield E(i,n,t.position==="before"?"beforeBegin":"afterEnd"):yield E(e,n),this._isAutoClosing(e)){let s=$(n.html).attr("data-value");$(e).find(`[data-value="${s}"] .accordion-collapse`).attr("data-bs-parent","#"+e.id)}})}_removeItem(e,t){let i=this._getItemInfo(e).filter(s=>t.target.indexOf(s.value)>-1),n=Shiny==null?void 0:Shiny.unbindAll;i.forEach(s=>{n&&n(s.item),s.item.remove()})}_updateItem(e,t){return h(this,null,function*(){let i=this._findItem(e,t.target);if(!i)throw new Error(`Unable to find an accordion_panel() with a value of ${t.target}`);if(w(t,"value")&&(i.dataset.value=t.value),w(t,"body")){let s=i.querySelector(".accordion-body");yield E(s,t.body)}let n=i.querySelector(".accordion-header");if(w(t,"title")){let s=n.querySelector(".accordion-title");yield E(s,t.title)}if(w(t,"icon")){let s=n.querySelector(".accordion-button > .accordion-icon");yield E(s,t.icon)}})}_getItemInfo(e){return Array.from(e.querySelectorAll(":scope > .accordion-item")).map(i=>this._getSingleItemInfo(i))}_getSingleItemInfo(e){let t=e.querySelector(".accordion-collapse"),i=()=>$(t).hasClass("show");return{item:e,value:e.dataset.value,isOpen:i,show:()=>{i()||$(t).collapse("show")},hide:()=>{i()&&$(t).collapse("hide")}}}_getValues(e,t,i){let n=i!==!0?i:t.map(l=>l.value);return this._isAutoClosing(e)&&(n=n.slice(n.length-1,n.length)),n}_findItem(e,t){return e.querySelector(`[data-value="${t}"]`)}_isAutoClosing(e){return e.classList.contains("autoclose")}};y(R,"accordion")});var S,z=f(()=>{"use strict";S=class{constructor(){this.resizeObserverEntries=[],this.resizeObserver=new ResizeObserver(e=>{let t=new Event("resize");if(window.dispatchEvent(t),!window.Shiny)return;let i=[];for(let n of e)n.target instanceof HTMLElement&&n.target.querySelector(".shiny-bound-output")&&n.target.querySelectorAll(".shiny-bound-output").forEach(s=>{if(i.includes(s))return;let{binding:l,onResize:d}=$(s).data("shinyOutputBinding");if(!l||!l.resize)return;let o=s.shinyResizeObserver;if(o&&o!==this||(o||(s.shinyResizeObserver=this),d(s),i.push(s),!s.classList.contains("shiny-plot-output")))return;let b=s.querySelector('img:not([width="100%"])');b&&b.setAttribute("width","100%")})})}observe(e){this.resizeObserver.observe(e),this.resizeObserverEntries.push(e)}unobserve(e){let t=this.resizeObserverEntries.indexOf(e);t<0||(this.resizeObserver.unobserve(e),this.resizeObserverEntries.splice(t,1))}flush(){this.resizeObserverEntries.forEach(e=>{document.body.contains(e)||this.unobserve(e)})}}});var I,q=f(()=>{"use strict";I=class{constructor(e,t){this.watching=new Set,this.observer=new MutationObserver(i=>{let n=new Set;for(let{type:s,removedNodes:l}of i)if(s==="childList"&&l.length!==0)for(let d of l)d instanceof HTMLElement&&(d.matches(e)&&n.add(d),d.querySelector(e)&&d.querySelectorAll(e).forEach(o=>n.add(o)));if(n.size!==0)for(let s of n)try{t(s)}catch(l){console.error(l)}})}observe(e){let t=this._flush();if(this.watching.has(e)){if(!t)return}else this.watching.add(e);t?this._restartObserver():this.observer.observe(e,{childList:!0,subtree:!0})}unobserve(e){this.watching.has(e)&&(this.watching.delete(e),this._flush(),this._restartObserver())}_restartObserver(){this.observer.disconnect();for(let e of this.watching)this.observer.observe(e,{childList:!0,subtree:!0})}_flush(){let e=!1,t=Array.from(this.watching);for(let i of t)document.body.contains(i)||(this.watching.delete(i),e=!0);return e}}});var a,g,D=f(()=>{"use strict";L();z();q();a=class{constructor(e){var t;e.removeAttribute(a.attr.ATTR_INIT),(t=e.querySelector(`script[${a.attr.ATTR_INIT}]`))==null||t.remove(),this.card=e,a.instanceMap.set(e,this),a.shinyResizeObserver.observe(this.card),a.cardRemovedObserver.observe(document.body),this._addEventListeners(),this.overlay=this._createOverlay(),this._setShinyInput(),this._exitFullScreenOnEscape=this._exitFullScreenOnEscape.bind(this),this._trapFocusExit=this._trapFocusExit.bind(this)}enterFullScreen(e){var t;e&&e.preventDefault(),this.card.id&&this.overlay.anchor.setAttribute("aria-controls",this.card.id),document.addEventListener("keydown",this._exitFullScreenOnEscape,!1),document.addEventListener("keydown",this._trapFocusExit,!0),this.card.setAttribute(a.attr.ATTR_FULL_SCREEN,"true"),document.body.classList.add(a.attr.CLASS_HAS_FULL_SCREEN),this.card.insertAdjacentElement("beforebegin",this.overlay.container),(!this.card.contains(document.activeElement)||(t=document.activeElement)!=null&&t.classList.contains(a.attr.CLASS_FULL_SCREEN_ENTER))&&(this.card.setAttribute("tabindex","-1"),this.card.focus()),this._emitFullScreenEvent(!0),this._setShinyInput()}exitFullScreen(){document.removeEventListener("keydown",this._exitFullScreenOnEscape,!1),document.removeEventListener("keydown",this._trapFocusExit,!0),this.overlay.container.remove(),this.card.setAttribute(a.attr.ATTR_FULL_SCREEN,"false"),this.card.removeAttribute("tabindex"),document.body.classList.remove(a.attr.CLASS_HAS_FULL_SCREEN),this._emitFullScreenEvent(!1),this._setShinyInput()}_setShinyInput(){if(!this.card.classList.contains(a.attr.CLASS_SHINY_INPUT)||!u)return;if(!u.setInputValue){setTimeout(()=>this._setShinyInput(),0);return}let e=this.card.getAttribute(a.attr.ATTR_FULL_SCREEN);u.setInputValue(this.card.id+"_full_screen",e==="true")}_emitFullScreenEvent(e){let t=new CustomEvent("bslib.card",{bubbles:!0,detail:{fullScreen:e}});this.card.dispatchEvent(t)}_addEventListeners(){let e=this.card.querySelector(`:scope > * > .${a.attr.CLASS_FULL_SCREEN_ENTER}`);e&&e.addEventListener("click",t=>this.enterFullScreen(t))}_exitFullScreenOnEscape(e){if(!(e.target instanceof HTMLElement))return;let t=["select[open]","input[aria-expanded='true']"];e.target.matches(t.join(", "))||e.key==="Escape"&&this.exitFullScreen()}_trapFocusExit(e){if(!(e instanceof KeyboardEvent)||e.key!=="Tab")return;let t=e.target===this.card,i=e.target===this.overlay.anchor,n=this.card.contains(e.target),s=()=>{e.preventDefault(),e.stopImmediatePropagation()};if(!(n||t||i)){s(),this.card.focus();return}let l=U(this.card).filter(A=>!A.classList.contains(a.attr.CLASS_FULL_SCREEN_ENTER));if(!(l.length>0)){s(),this.overlay.anchor.focus();return}if(t)return;let o=l[l.length-1],b=e.target===o;if(i&&e.shiftKey){s(),o.focus();return}if(b&&!e.shiftKey){s(),this.overlay.anchor.focus();return}}_createOverlay(){let e=document.createElement("div");e.id=a.attr.ID_FULL_SCREEN_OVERLAY,e.onclick=this.exitFullScreen.bind(this);let t=this._createOverlayCloseAnchor();return e.appendChild(t),{container:e,anchor:t}}_createOverlayCloseAnchor(){let e=document.createElement("a");return e.classList.add(a.attr.CLASS_FULL_SCREEN_EXIT),e.tabIndex=0,e.setAttribute("aria-expanded","true"),e.setAttribute("aria-label","Close card"),e.setAttribute("role","button"),e.onclick=t=>{this.exitFullScreen(),t.stopPropagation()},e.onkeydown=t=>{(t.key==="Enter"||t.key===" ")&&this.exitFullScreen()},e.innerHTML=this._overlayCloseHtml(),e}_overlayCloseHtml(){return"Close "}static getInstance(e){return a.instanceMap.get(e)}static initializeAllCards(e=!0){if(document.readyState==="loading"){a.onReadyScheduled||(a.onReadyScheduled=!0,document.addEventListener("DOMContentLoaded",()=>{a.initializeAllCards(!1)}));return}e&&a.shinyResizeObserver.flush();let t=`.${a.attr.CLASS_CARD}[${a.attr.ATTR_INIT}]`;if(!document.querySelector(t))return;document.querySelectorAll(t).forEach(n=>new a(n))}},g=a;g.attr={ATTR_INIT:"data-bslib-card-init",CLASS_CARD:"bslib-card",ATTR_FULL_SCREEN:"data-full-screen",CLASS_HAS_FULL_SCREEN:"bslib-has-full-screen",CLASS_FULL_SCREEN_ENTER:"bslib-full-screen-enter",CLASS_FULL_SCREEN_EXIT:"bslib-full-screen-exit",ID_FULL_SCREEN_OVERLAY:"bslib-full-screen-overlay",CLASS_SHINY_INPUT:"bslib-card-input"},g.shinyResizeObserver=new S,g.cardRemovedObserver=new I(`.${a.attr.CLASS_CARD}`,e=>{let t=a.getInstance(e);t&&t.card.getAttribute(a.attr.ATTR_FULL_SCREEN)==="true"&&t.exitFullScreen()}),g.instanceMap=new WeakMap,g.onReadyScheduled=!1;_("Card",g)});var c,p,F,P=f(()=>{"use strict";L();z();c=class{constructor(e){this.windowSize="";var n;c.instanceMap.set(e,this),this.layout={container:e,main:e.querySelector(":scope > .main"),sidebar:e.querySelector(":scope > .sidebar"),toggle:e.querySelector(":scope > .collapse-toggle")};let t=this.layout.sidebar.querySelector(":scope > .sidebar-content > .accordion");t&&((n=t==null?void 0:t.parentElement)==null||n.classList.add("has-accordion"),t.classList.add("accordion-flush")),this._initSidebarCounters(),this._initSidebarState(),(this._isCollapsible("desktop")||this._isCollapsible("mobile"))&&this._initEventListeners(),c.shinyResizeObserver.observe(this.layout.main),e.removeAttribute("data-bslib-sidebar-init");let i=e.querySelector(":scope > script[data-bslib-sidebar-init]");i&&e.removeChild(i)}get isClosed(){return this.layout.container.classList.contains(c.classes.COLLAPSE)}static getInstance(e){return c.instanceMap.get(e)}_isCollapsible(e="desktop"){let{container:t}=this.layout,i=e==="desktop"?"collapsibleDesktop":"collapsibleMobile",n=t.dataset[i];return n===void 0?!0:n.trim().toLowerCase()!=="false"}static initCollapsibleAll(e=!0){if(document.readyState==="loading"){c.onReadyScheduled||(c.onReadyScheduled=!0,document.addEventListener("DOMContentLoaded",()=>{c.initCollapsibleAll(!1)}));return}let t=`.${c.classes.LAYOUT}[data-bslib-sidebar-init]`;if(!document.querySelector(t))return;e&&c.shinyResizeObserver.flush(),document.querySelectorAll(t).forEach(n=>new c(n))}_initEventListeners(){var t;let{toggle:e}=this.layout;e.addEventListener("click",i=>{i.preventDefault(),this.toggle("toggle")}),(t=e.querySelector(".collapse-icon"))==null||t.addEventListener("transitionend",()=>this._finalizeState()),!(this._isCollapsible("desktop")&&this._isCollapsible("mobile"))&&window.addEventListener("resize",()=>this._handleWindowResizeEvent())}_initSidebarCounters(){let{container:e}=this.layout,t=`.${c.classes.LAYOUT}> .main > .${c.classes.LAYOUT}:not([data-bslib-sidebar-open="always"])`;if(!(e.querySelector(t)===null))return;function n(o){return o=o?o.parentElement:null,o&&o.classList.contains("main")&&(o=o.parentElement),o&&o.classList.contains(c.classes.LAYOUT)?o:null}let s=[e],l=n(e);for(;l;)s.unshift(l),l=n(l);let d={left:0,right:0};s.forEach(function(o){let A=o.classList.contains("sidebar-right")?d.right++:d.left++;o.style.setProperty("--_js-toggle-count-this-side",A.toString()),o.style.setProperty("--_js-toggle-count-max-side",Math.max(d.right,d.left).toString())})}_getWindowSize(){let{container:e}=this.layout;return window.getComputedStyle(e).getPropertyValue("--bslib-sidebar-js-window-size").trim()}_initialToggleState(){var n,s;let{container:e}=this.layout,t=this.windowSize==="desktop"?"openDesktop":"openMobile",i=(s=(n=e.dataset[t])==null?void 0:n.trim())==null?void 0:s.toLowerCase();return i===void 0||["open","always"].includes(i)?"open":["close","closed"].includes(i)?"close":"open"}_initSidebarState(){this.windowSize=this._getWindowSize();let e=this._initialToggleState();this.toggle(e,!0)}_handleWindowResizeEvent(){let e=this._getWindowSize();!e||e==this.windowSize||this._initSidebarState()}toggle(e,t=!1){typeof e=="undefined"?e="toggle":e==="closed"&&(e="close");let{container:i,sidebar:n}=this.layout,s=this.isClosed;if(["open","close","toggle"].indexOf(e)===-1)throw new Error(`Unknown method ${e}`);if(e==="toggle"&&(e=s?"open":"close"),s&&e==="close"||!s&&e==="open"){t&&this._finalizeState();return}e==="open"&&(n.hidden=!1),i.classList.toggle(c.classes.TRANSITIONING,!t),i.classList.toggle(c.classes.COLLAPSE),t&&this._finalizeState()}_finalizeState(){let{container:e,sidebar:t,toggle:i}=this.layout;e.classList.remove(c.classes.TRANSITIONING),t.hidden=this.isClosed,i.setAttribute("aria-expanded",this.isClosed?"false":"true");let n=new CustomEvent("bslib.sidebar",{bubbles:!0,detail:{open:!this.isClosed}});t.dispatchEvent(n),$(t).trigger("toggleCollapse.sidebarInputBinding"),$(t).trigger(this.isClosed?"hidden":"shown")}},p=c;p.shinyResizeObserver=new S,p.classes={LAYOUT:"bslib-sidebar-layout",COLLAPSE:"sidebar-collapsed",TRANSITIONING:"transitioning"},p.onReadyScheduled=!1,p.instanceMap=new WeakMap;F=class extends m{find(e){return $(e).find(`.${p.classes.LAYOUT} > .bslib-sidebar-input`)}getValue(e){let t=p.getInstance(e.parentElement);return t?!t.isClosed:!1}setValue(e,t){let i=t?"open":"close";this.receiveMessage(e,{method:i})}subscribe(e,t){$(e).on("toggleCollapse.sidebarInputBinding",function(i){t(!0)})}unsubscribe(e){$(e).off(".sidebarInputBinding")}receiveMessage(e,t){let i=p.getInstance(e.parentElement);i&&i.toggle(t.method)}};y(F,"sidebar");_("Sidebar",p)});var T,M,C,x,N,W=f(()=>{"use strict";L();N=class extends m{constructor(){super(...arguments);H(this,C);H(this,T,new WeakMap);H(this,M,new WeakMap)}find(t){return $(t).find(".bslib-task-button")}getValue(t){var i;return{value:(i=v(this,T).get(t))!=null?i:0,autoReset:t.hasAttribute("data-auto-reset")}}getType(){return"bslib.taskbutton"}subscribe(t,i){v(this,M).has(t)&&this.unsubscribe(t);let n=()=>{var s;v(this,T).set(t,((s=v(this,T).get(t))!=null?s:0)+1),i(!0),O(this,C,x).call(this,t,"busy")};v(this,M).set(t,n),t.addEventListener("click",n)}unsubscribe(t){let i=v(this,M).get(t);i&&t.removeEventListener("click",i)}receiveMessage(n,s){return h(this,arguments,function*(t,{state:i}){O(this,C,x).call(this,t,i)})}};T=new WeakMap,M=new WeakMap,C=new WeakSet,x=function(t,i){t.disabled=i==="busy";let n=t.querySelector("bslib-switch-inline");n&&(n.case=i)};y(N,"task-button")});function V(r){if(window.Shiny)for(let[e,t]of Object.entries(r))Shiny.addCustomMessageHandler(e,t)}var j=f(()=>{"use strict"});var Z=X(Y=>{B();D();P();W();L();j();var K={"bslib.toggle-input-binary":r=>h(Y,null,function*(){let e=document.getElementById(r.id);e||console.warn("[bslib.toggle-input-binary] No element found",r);let t=$(e).data("shiny-input-binding");if(!(t instanceof m)){console.warn("[bslib.toggle-input-binary] No input binding found",r);return}let i=r.value;typeof i=="undefined"&&(i=!t.getValue(e)),yield t.receiveMessage(e,{value:i})})};window.Shiny&&V(K);function G(){let r=document.createElement("div");r.innerHTML=` `,document.body.appendChild(r.children[0])}document.readyState==="complete"?Y():document.addEventListener("DOMContentLoaded",Y)});K();})(); + `,document.body.appendChild(r.children[0])}document.readyState==="complete"?G():document.addEventListener("DOMContentLoaded",G)});Z();})(); //# sourceMappingURL=components.min.js.map diff --git a/shiny/www/shared/bslib/components/components.min.js.map b/shiny/www/shared/bslib/components/components.min.js.map index b1c4689d6..5762afdba 100644 --- a/shiny/www/shared/bslib/components/components.min.js.map +++ b/shiny/www/shared/bslib/components/components.min.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../srcts/src/components/_utils.ts", "../../../srcts/src/components/accordion.ts", "../../../srcts/src/components/_shinyResizeObserver.ts", "../../../srcts/src/components/_shinyRemovedObserver.ts", "../../../srcts/src/components/card.ts", "../../../srcts/src/components/sidebar.ts", "../../../srcts/src/components/taskButton.ts", "../../../srcts/src/components/_shinyAddCustomMessageHandlers.ts", "../../../srcts/src/components/index.ts"], - "sourcesContent": ["import type { HtmlDep } from \"rstudio-shiny/srcts/types/src/shiny/render\";\n\nimport type { InputBinding as InputBindingType } from \"rstudio-shiny/srcts/types/src/bindings/input\";\n\n// eslint-disable-next-line @typescript-eslint/naming-convention\nconst Shiny: typeof window.Shiny | undefined = window.Shiny;\n\n// Exclude undefined from T\ntype NotUndefined = T extends undefined ? never : T;\n\n// eslint-disable-next-line @typescript-eslint/naming-convention\nconst InputBinding = (\n Shiny ? Shiny.InputBinding : class {}\n) as typeof InputBindingType;\n\nfunction registerBinding(\n inputBindingClass: new () => InputBindingType,\n name: string\n): void {\n if (Shiny) {\n Shiny.inputBindings.register(new inputBindingClass(), \"bslib.\" + name);\n }\n}\n\n// Return true if the key exists on the object and the value is not undefined.\n//\n// This method is mainly used in input bindings' `receiveMessage` method.\n// Since we know that the values are sent by Shiny via `{jsonlite}`,\n// then we know that there are no `undefined` values. `null` is possible, but not `undefined`.\nfunction hasDefinedProperty<\n Prop extends keyof X,\n X extends { [key: string]: any }\n>(\n obj: X,\n prop: Prop\n): obj is X & { [key in NonNullable]: NotUndefined } {\n return (\n Object.prototype.hasOwnProperty.call(obj, prop) && obj[prop] !== undefined\n );\n}\n\n// TODO: Shiny should trigger resize events when the output\n// https://github.com/rstudio/shiny/pull/3682\nfunction doWindowResizeOnElementResize(el: HTMLElement): void {\n if ($(el).data(\"window-resize-observer\")) {\n return;\n }\n const resizeEvent = new Event(\"resize\");\n const ro = new ResizeObserver(() => {\n window.dispatchEvent(resizeEvent);\n });\n ro.observe(el);\n $(el).data(\"window-resize-observer\", ro);\n}\n\nfunction getAllFocusableChildren(el: HTMLElement): HTMLElement[] {\n // Cross-referenced with https://allyjs.io/data-tables/focusable.html\n const base = [\n \"a[href]\",\n \"area[href]\",\n \"button\",\n \"details summary\",\n \"input\",\n \"iframe\",\n \"select\",\n \"textarea\",\n '[contentEditable=\"\"]',\n '[contentEditable=\"true\"]',\n '[contentEditable=\"TRUE\"]',\n \"[tabindex]\",\n ];\n const modifiers = [':not([tabindex=\"-1\"])', \":not([disabled])\"];\n const selectors = base.map((b) => b + modifiers.join(\"\"));\n const focusable = el.querySelectorAll(selectors.join(\", \"));\n return Array.from(focusable) as HTMLElement[];\n}\n\nasync function shinyRenderContent(\n ...args: Parameters\n): ReturnType {\n if (!Shiny) {\n throw new Error(\"This function must be called in a Shiny app.\");\n }\n if (Shiny.renderContentAsync) {\n return await Shiny.renderContentAsync.apply(null, args);\n } else {\n return await Shiny.renderContent.apply(null, args);\n }\n}\n\nexport {\n InputBinding,\n registerBinding,\n hasDefinedProperty,\n doWindowResizeOnElementResize,\n getAllFocusableChildren,\n shinyRenderContent,\n Shiny,\n};\nexport type { HtmlDep };\n", "import type { HtmlDep } from \"./_utils\";\nimport {\n InputBinding,\n registerBinding,\n hasDefinedProperty,\n shinyRenderContent,\n} from \"./_utils\";\n\ntype AccordionItem = {\n item: HTMLElement;\n value: string;\n isOpen: () => boolean;\n show: () => void;\n hide: () => void;\n};\n\ntype HTMLContent = {\n html: string;\n deps?: HtmlDep[];\n};\n\ntype SetMessage = {\n method: \"set\";\n values: string[];\n};\n\ntype OpenMessage = {\n method: \"open\";\n values: string[] | true;\n};\n\ntype CloseMessage = {\n method: \"close\";\n values: string[] | true;\n};\n\ntype InsertMessage = {\n method: \"insert\";\n panel: HTMLContent;\n target: string;\n position: \"after\" | \"before\";\n};\n\ntype RemoveMessage = {\n method: \"remove\";\n target: string[];\n};\n\ntype UpdateMessage = {\n method: \"update\";\n target: string;\n value: string;\n body: HTMLContent;\n title: HTMLContent;\n icon: HTMLContent;\n};\n\ntype MessageData =\n | CloseMessage\n | InsertMessage\n | OpenMessage\n | RemoveMessage\n | SetMessage\n | UpdateMessage;\n\nclass AccordionInputBinding extends InputBinding {\n find(scope: HTMLElement) {\n return $(scope).find(\".accordion.bslib-accordion-input\");\n }\n\n getValue(el: HTMLElement): string[] | null {\n const items = this._getItemInfo(el);\n const selected = items.filter((x) => x.isOpen()).map((x) => x.value);\n return selected.length === 0 ? null : selected;\n }\n\n subscribe(el: HTMLElement, callback: (x: boolean) => void) {\n $(el).on(\n \"shown.bs.collapse.accordionInputBinding hidden.bs.collapse.accordionInputBinding\",\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n function (event) {\n callback(true);\n }\n );\n }\n\n unsubscribe(el: HTMLElement) {\n $(el).off(\".accordionInputBinding\");\n }\n\n async receiveMessage(el: HTMLElement, data: MessageData) {\n const method = data.method;\n if (method === \"set\") {\n this._setItems(el, data);\n } else if (method === \"open\") {\n this._openItems(el, data);\n } else if (method === \"close\") {\n this._closeItems(el, data);\n } else if (method === \"remove\") {\n this._removeItem(el, data);\n } else if (method === \"insert\") {\n await this._insertItem(el, data);\n } else if (method === \"update\") {\n await this._updateItem(el, data);\n } else {\n throw new Error(`Method not yet implemented: ${method}`);\n }\n }\n\n protected _setItems(el: HTMLElement, data: SetMessage) {\n const items = this._getItemInfo(el);\n const vals = this._getValues(el, items, data.values);\n items.forEach((x) => {\n vals.indexOf(x.value) > -1 ? x.show() : x.hide();\n });\n }\n\n protected _openItems(el: HTMLElement, data: OpenMessage) {\n const items = this._getItemInfo(el);\n const vals = this._getValues(el, items, data.values);\n items.forEach((x) => {\n if (vals.indexOf(x.value) > -1) x.show();\n });\n }\n\n protected _closeItems(el: HTMLElement, data: CloseMessage) {\n const items = this._getItemInfo(el);\n const vals = this._getValues(el, items, data.values);\n items.forEach((x) => {\n if (vals.indexOf(x.value) > -1) x.hide();\n });\n }\n\n protected async _insertItem(el: HTMLElement, data: InsertMessage) {\n let targetItem = this._findItem(el, data.target);\n\n // If no target was specified, or the target was not found, then default\n // to the first or last item, depending on the position\n if (!targetItem) {\n targetItem = (\n data.position === \"before\" ? el.firstElementChild : el.lastElementChild\n ) as HTMLElement;\n }\n\n const panel = data.panel;\n\n // If there is still no targetItem, then there are no items in the accordion\n if (targetItem) {\n await shinyRenderContent(\n targetItem,\n panel,\n data.position === \"before\" ? \"beforeBegin\" : \"afterEnd\"\n );\n } else {\n await shinyRenderContent(el, panel);\n }\n\n // Need to add a reference to the parent id that makes autoclose to work\n if (this._isAutoClosing(el)) {\n const val = $(panel.html).attr(\"data-value\");\n $(el)\n .find(`[data-value=\"${val}\"] .accordion-collapse`)\n .attr(\"data-bs-parent\", \"#\" + el.id);\n }\n }\n\n protected _removeItem(el: HTMLElement, data: RemoveMessage) {\n const targetItems = this._getItemInfo(el).filter(\n (x) => data.target.indexOf(x.value) > -1\n );\n\n const unbindAll = Shiny?.unbindAll;\n\n targetItems.forEach((x) => {\n if (unbindAll) unbindAll(x.item);\n x.item.remove();\n });\n }\n\n protected async _updateItem(el: HTMLElement, data: UpdateMessage) {\n const target = this._findItem(el, data.target);\n\n if (!target) {\n throw new Error(\n `Unable to find an accordion_panel() with a value of ${data.target}`\n );\n }\n\n if (hasDefinedProperty(data, \"value\")) {\n target.dataset.value = data.value;\n }\n\n if (hasDefinedProperty(data, \"body\")) {\n const body = target.querySelector(\".accordion-body\") as HTMLElement; // always exists\n await shinyRenderContent(body, data.body);\n }\n\n const header = target.querySelector(\".accordion-header\") as HTMLElement; // always exists\n\n if (hasDefinedProperty(data, \"title\")) {\n const title = header.querySelector(\".accordion-title\") as HTMLElement; // always exists\n await shinyRenderContent(title, data.title);\n }\n\n if (hasDefinedProperty(data, \"icon\")) {\n const icon = header.querySelector(\n \".accordion-button > .accordion-icon\"\n ) as HTMLElement; // always exists\n await shinyRenderContent(icon, data.icon);\n }\n }\n\n protected _getItemInfo(el: HTMLElement): AccordionItem[] {\n const items = Array.from(\n el.querySelectorAll(\":scope > .accordion-item\")\n ) as HTMLElement[];\n return items.map((x) => this._getSingleItemInfo(x));\n }\n\n protected _getSingleItemInfo(x: HTMLElement): AccordionItem {\n const collapse = x.querySelector(\".accordion-collapse\") as HTMLElement;\n const isOpen = () => $(collapse).hasClass(\"show\");\n return {\n item: x,\n value: x.dataset.value as string,\n isOpen: isOpen,\n show: () => {\n if (!isOpen()) $(collapse).collapse(\"show\");\n },\n hide: () => {\n if (isOpen()) $(collapse).collapse(\"hide\");\n },\n };\n }\n\n protected _getValues(\n el: HTMLElement,\n items: AccordionItem[],\n values: string[] | true\n ): string[] {\n let vals = values !== true ? values : items.map((x) => x.value);\n const autoclose = this._isAutoClosing(el);\n if (autoclose) {\n vals = vals.slice(vals.length - 1, vals.length);\n }\n return vals;\n }\n\n protected _findItem(el: HTMLElement, value: string): HTMLElement | null {\n return el.querySelector(`[data-value=\"${value}\"]`);\n }\n\n protected _isAutoClosing(el: HTMLElement): boolean {\n return el.classList.contains(\"autoclose\");\n }\n}\n\nregisterBinding(AccordionInputBinding, \"accordion\");\n", "/**\n * A resize observer that ensures Shiny outputs resize during or just after\n * their parent container size changes. Useful, in particular, for sidebar\n * transitions or for full-screen card transitions.\n *\n * @class ShinyResizeObserver\n * @typedef {ShinyResizeObserver}\n */\nclass ShinyResizeObserver {\n /**\n * The actual ResizeObserver instance.\n * @private\n * @type {ResizeObserver}\n */\n private resizeObserver: ResizeObserver;\n /**\n * An array of elements that are currently being watched by the Resize\n * Observer.\n *\n * @details\n * We don't currently have lifecycle hooks that allow us to unobserve elements\n * when they are removed from the DOM. As a result, we need to manually check\n * that the elements we're watching still exist in the DOM. This array keeps\n * track of the elements we're watching so that we can check them later.\n * @private\n * @type {HTMLElement[]}\n */\n private resizeObserverEntries: HTMLElement[];\n\n /**\n * Watch containers for size changes and ensure that Shiny outputs and\n * htmlwidgets within resize appropriately.\n *\n * @details\n * The ShinyResizeObserver is used to watch the containers, such as Sidebars\n * and Cards for size changes, in particular when the sidebar state is toggled\n * or the card body is expanded full screen. It performs two primary tasks:\n *\n * 1. Dispatches a `resize` event on the window object. This is necessary to\n * ensure that Shiny outputs resize appropriately. In general, the window\n * resizing is throttled and the output update occurs when the transition\n * is complete.\n * 2. If an output with a resize method on the output binding is detected, we\n * directly call the `.onResize()` method of the binding. This ensures that\n * htmlwidgets transition smoothly. In static mode, htmlwidgets does this\n * already.\n *\n * @note\n * This resize observer also handles race conditions in some complex\n * fill-based layouts with multiple outputs (e.g., plotly), where shiny\n * initializes with the correct sizing, but in-between the 1st and last\n * renderValue(), the size of the output containers can change, meaning every\n * output but the 1st gets initialized with the wrong size during their\n * renderValue(). Then, after the render phase, shiny won't know to trigger a\n * resize since all the widgets will return to their original size (and thus,\n * Shiny thinks there isn't any resizing to do). The resize observer works\n * around this by ensuring that the output is resized whenever its container\n * size changes.\n * @constructor\n */\n constructor() {\n this.resizeObserverEntries = [];\n this.resizeObserver = new ResizeObserver((entries) => {\n const resizeEvent = new Event(\"resize\");\n window.dispatchEvent(resizeEvent);\n\n // the rest of this callback is only relevant in Shiny apps\n if (!window.Shiny) return;\n\n const resized = [] as HTMLElement[];\n\n for (const entry of entries) {\n if (!(entry.target instanceof HTMLElement)) continue;\n if (!entry.target.querySelector(\".shiny-bound-output\")) continue;\n\n entry.target\n .querySelectorAll(\".shiny-bound-output\")\n .forEach((el) => {\n if (resized.includes(el)) return;\n\n const { binding, onResize } = $(el).data(\"shinyOutputBinding\");\n if (!binding || !binding.resize) return;\n\n // if this output is owned by another observer, skip it\n const owner = (el as any).shinyResizeObserver;\n if (owner && owner !== this) return;\n // mark this output as owned by this shinyResizeObserver instance\n if (!owner) (el as any).shinyResizeObserver = this;\n\n // trigger immediate resizing of outputs with a resize method\n onResize(el);\n // only once per output and resize event\n resized.push(el);\n\n // set plot images to 100% width temporarily during the transition\n if (!el.classList.contains(\"shiny-plot-output\")) return;\n const img = el.querySelector(\n 'img:not([width=\"100%\"])'\n );\n if (img) img.setAttribute(\"width\", \"100%\");\n });\n }\n });\n }\n\n /**\n * Observe an element for size changes.\n * @param {HTMLElement} el - The element to observe.\n */\n observe(el: HTMLElement): void {\n this.resizeObserver.observe(el);\n this.resizeObserverEntries.push(el);\n }\n\n /**\n * Stop observing an element for size changes.\n * @param {HTMLElement} el - The element to stop observing.\n */\n unobserve(el: HTMLElement): void {\n const idxEl = this.resizeObserverEntries.indexOf(el);\n if (idxEl < 0) return;\n\n this.resizeObserver.unobserve(el);\n this.resizeObserverEntries.splice(idxEl, 1);\n }\n\n /**\n * This method checks that we're not continuing to watch elements that no\n * longer exist in the DOM. If any are found, we stop observing them and\n * remove them from our array of observed elements.\n *\n * @private\n * @static\n */\n flush(): void {\n this.resizeObserverEntries.forEach((el) => {\n if (!document.body.contains(el)) this.unobserve(el);\n });\n }\n}\n\nexport { ShinyResizeObserver };\n", "type Callback = (el: T) => void;\n\n/**\n * Watch for the removal of specific elements from regions of the page.\n */\nexport class ShinyRemovedObserver {\n private observer: MutationObserver;\n private watching: Set;\n\n /**\n * Creates a new instance of the `ShinyRemovedObserver` class to watch for the\n * removal of specific elements from part of the DOM.\n *\n * @param selector A CSS selector to identify elements to watch for removal.\n * @param callback The function to be called on a matching element when it\n * is removed.\n */\n constructor(selector: string, callback: Callback) {\n this.watching = new Set();\n this.observer = new MutationObserver((mutations) => {\n const found = new Set();\n for (const { type, removedNodes } of mutations) {\n if (type !== \"childList\") continue;\n if (removedNodes.length === 0) continue;\n\n for (const node of removedNodes) {\n if (!(node instanceof HTMLElement)) continue;\n if (node.matches(selector)) {\n found.add(node);\n }\n if (node.querySelector(selector)) {\n node\n .querySelectorAll(selector)\n .forEach((el) => found.add(el));\n }\n }\n }\n if (found.size === 0) return;\n for (const el of found) {\n try {\n callback(el);\n } catch (e) {\n console.error(e);\n }\n }\n });\n }\n\n /**\n * Starts observing the specified element for removal of its children. If the\n * element is already being observed, no change is made to the mutation\n * observer.\n * @param el The element to observe.\n */\n observe(el: HTMLElement): void {\n const changed = this._flush();\n if (this.watching.has(el)) {\n if (!changed) return;\n } else {\n this.watching.add(el);\n }\n\n if (changed) {\n this._restartObserver();\n } else {\n this.observer.observe(el, { childList: true, subtree: true });\n }\n }\n\n /**\n * Stops observing the specified element for removal.\n * @param el The element to unobserve.\n */\n unobserve(el: HTMLElement): void {\n if (!this.watching.has(el)) return;\n // MutationObserver doesn't have an \"unobserve\" method, so we have to\n // disconnect and re-observe all elements that are still being watched.\n this.watching.delete(el);\n this._flush();\n this._restartObserver();\n }\n\n /**\n * Restarts the mutation observer, observing all elements in the `watching`\n * and implicitly unobserving any elements that are no longer in the\n * watchlist.\n * @private\n */\n private _restartObserver(): void {\n this.observer.disconnect();\n for (const el of this.watching) {\n this.observer.observe(el, { childList: true, subtree: true });\n }\n }\n\n /**\n * Flushes the set of watched elements, removing any elements that are no\n * longer in the DOM, but it does not modify the mutation observer.\n * @private\n * @returns A boolean indicating whether the watched elements have changed.\n */\n private _flush(): boolean {\n let watchedChanged = false;\n const watched = Array.from(this.watching);\n for (const el of watched) {\n if (document.body.contains(el)) continue;\n this.watching.delete(el);\n watchedChanged = true;\n }\n return watchedChanged;\n }\n}\n", "import { getAllFocusableChildren, Shiny } from \"./_utils\";\nimport { ShinyResizeObserver } from \"./_shinyResizeObserver\";\nimport { ShinyRemovedObserver } from \"./_shinyRemovedObserver\";\n\n/**\n * The overlay element that is placed behind the card when expanded full screen.\n *\n * @interface CardFullScreenOverlay\n * @typedef {CardFullScreenOverlay}\n */\ninterface CardFullScreenOverlay {\n /**\n * The full screen overlay container.\n * @type {HTMLDivElement}\n */\n container: HTMLDivElement;\n /**\n * The anchor element used to close the full screen overlay.\n * @type {HTMLAnchorElement}\n */\n anchor: HTMLAnchorElement;\n}\n\n/**\n * The bslib card component class.\n *\n * @class Card\n * @typedef {Card}\n */\nclass Card {\n /**\n * The card container element.\n * @private\n * @type {HTMLElement}\n */\n private card: HTMLElement;\n /**\n * The card's full screen overlay element. We create this element once and add\n * and remove it from the DOM as needed (this simplifies focus management\n * while in full screen mode).\n * @private\n * @type {CardFullScreenOverlay}\n */\n private overlay: CardFullScreenOverlay;\n\n /**\n * Key bslib-specific classes and attributes used by the card component.\n * @private\n * @static\n */\n private static attr = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n ATTR_INIT: \"data-bslib-card-init\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n CLASS_CARD: \"bslib-card\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n ATTR_FULL_SCREEN: \"data-full-screen\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n CLASS_HAS_FULL_SCREEN: \"bslib-has-full-screen\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n CLASS_FULL_SCREEN_ENTER: \"bslib-full-screen-enter\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n CLASS_FULL_SCREEN_EXIT: \"bslib-full-screen-exit\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n ID_FULL_SCREEN_OVERLAY: \"bslib-full-screen-overlay\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n CLASS_SHINY_INPUT: \"bslib-card-input\",\n };\n\n /**\n * A Shiny-specific resize observer that ensures Shiny outputs in within the\n * card resize appropriately.\n * @private\n * @type {ShinyResizeObserver}\n * @static\n */\n private static shinyResizeObserver = new ShinyResizeObserver();\n\n /**\n * Watch card parent containers for removal and exit full screen mode if a\n * full screen card is removed from the DOM.\n *\n * @private\n * @type {ShinyRemovedObserver}\n * @static\n */\n private static cardRemovedObserver = new ShinyRemovedObserver(\n `.${Card.attr.CLASS_CARD}`,\n (el) => {\n const card = Card.getInstance(el);\n if (!card) return;\n if (card.card.getAttribute(Card.attr.ATTR_FULL_SCREEN) === \"true\") {\n card.exitFullScreen();\n }\n }\n );\n\n /**\n * Creates an instance of a bslib Card component.\n *\n * @constructor\n * @param {HTMLElement} card\n */\n constructor(card: HTMLElement) {\n // remove initialization attribute and script\n card.removeAttribute(Card.attr.ATTR_INIT);\n card\n .querySelector(`script[${Card.attr.ATTR_INIT}]`)\n ?.remove();\n\n this.card = card;\n Card.instanceMap.set(card, this);\n\n // Let Shiny know to trigger resize when the card size changes\n // TODO: shiny could/should do this itself (rstudio/shiny#3682)\n Card.shinyResizeObserver.observe(this.card);\n Card.cardRemovedObserver.observe(document.body);\n\n this._addEventListeners();\n this.overlay = this._createOverlay();\n this._setShinyInput();\n\n // bind event handler methods to this card instance\n this._exitFullScreenOnEscape = this._exitFullScreenOnEscape.bind(this);\n this._trapFocusExit = this._trapFocusExit.bind(this);\n }\n\n /**\n * Enter the card's full screen mode, either programmatically or via an event\n * handler. Full screen mode is activated by adding a class to the card that\n * positions it absolutely and expands it to fill the viewport. In addition,\n * we add a full screen overlay element behind the card and we trap focus in\n * the expanded card while in full screen mode.\n *\n * @param {?Event} [event]\n */\n enterFullScreen(event?: Event): void {\n if (event) event.preventDefault();\n\n // Update close anchor to control current expanded card\n if (this.card.id) {\n this.overlay.anchor.setAttribute(\"aria-controls\", this.card.id);\n }\n\n document.addEventListener(\"keydown\", this._exitFullScreenOnEscape, false);\n\n // trap focus in the fullscreen container, listening for Tab key on the\n // capture phase so we have the best chance of preventing other handlers\n document.addEventListener(\"keydown\", this._trapFocusExit, true);\n\n this.card.setAttribute(Card.attr.ATTR_FULL_SCREEN, \"true\");\n document.body.classList.add(Card.attr.CLASS_HAS_FULL_SCREEN);\n this.card.insertAdjacentElement(\"beforebegin\", this.overlay.container);\n\n // Set initial focus on the card, if not already\n if (\n !this.card.contains(document.activeElement) ||\n document.activeElement?.classList.contains(\n Card.attr.CLASS_FULL_SCREEN_ENTER\n )\n ) {\n this.card.setAttribute(\"tabindex\", \"-1\");\n this.card.focus();\n }\n\n this._emitFullScreenEvent(true);\n this._setShinyInput();\n }\n\n /**\n * Exit full screen mode. This removes the full screen overlay element,\n * removes the full screen class from the card, and removes the keyboard event\n * listeners that were added when entering full screen mode.\n */\n exitFullScreen(): void {\n document.removeEventListener(\n \"keydown\",\n this._exitFullScreenOnEscape,\n false\n );\n document.removeEventListener(\"keydown\", this._trapFocusExit, true);\n\n // Remove overlay and remove full screen classes from card\n this.overlay.container.remove();\n this.card.setAttribute(Card.attr.ATTR_FULL_SCREEN, \"false\");\n this.card.removeAttribute(\"tabindex\");\n document.body.classList.remove(Card.attr.CLASS_HAS_FULL_SCREEN);\n\n this._emitFullScreenEvent(false);\n this._setShinyInput();\n }\n\n private _setShinyInput(): void {\n if (!this.card.classList.contains(Card.attr.CLASS_SHINY_INPUT)) return;\n if (!Shiny) return;\n if (!Shiny.setInputValue) {\n // Shiny isn't ready yet, so we'll try to set the input value again later,\n // (but it might not be ready then either, so we'll keep trying).\n setTimeout(() => this._setShinyInput(), 0);\n return;\n }\n const fsAttr = this.card.getAttribute(Card.attr.ATTR_FULL_SCREEN);\n Shiny.setInputValue(this.card.id + \"_full_screen\", fsAttr === \"true\");\n }\n\n /**\n * Emits a custom event to communicate the card's full screen state change.\n * @private\n * @param {boolean} fullScreen\n */\n private _emitFullScreenEvent(fullScreen: boolean): void {\n const event = new CustomEvent(\"bslib.card\", {\n bubbles: true,\n detail: { fullScreen },\n });\n this.card.dispatchEvent(event);\n }\n\n /**\n * Adds general card-specific event listeners.\n * @private\n */\n private _addEventListeners(): void {\n const btnFullScreen = this.card.querySelector(\n `:scope > * > .${Card.attr.CLASS_FULL_SCREEN_ENTER}`\n );\n if (!btnFullScreen) return;\n btnFullScreen.addEventListener(\"click\", (ev) => this.enterFullScreen(ev));\n }\n\n /**\n * An event handler to exit full screen mode when the Escape key is pressed.\n * @private\n * @param {KeyboardEvent} event\n */\n private _exitFullScreenOnEscape(event: KeyboardEvent): void {\n if (!(event.target instanceof HTMLElement)) return;\n // If the user is in the middle of a select input choice, don't exit\n const selOpenSelectInput = [\"select[open]\", \"input[aria-expanded='true']\"];\n if (event.target.matches(selOpenSelectInput.join(\", \"))) return;\n\n if (event.key === \"Escape\") {\n this.exitFullScreen();\n }\n }\n\n /**\n * An event handler to trap focus within the card when in full screen mode.\n *\n * @description\n * This keyboard event handler ensures that tab focus stays within the card\n * when in full screen mode. When the card is first expanded,\n * we move focus to the card element itself. If focus somehow leaves the card,\n * we returns focus to the card container.\n *\n * Within the card, we handle only tabbing from the close anchor or the last\n * focusable element and only when tab focus would have otherwise left the\n * card. In those cases, we cycle focus to the last focusable element or back\n * to the anchor. If the card doesn't have any focusable elements, we move\n * focus to the close anchor.\n *\n * @note\n * Because the card contents may change, we check for focusable elements\n * every time the handler is called.\n *\n * @private\n * @param {KeyboardEvent} event\n */\n private _trapFocusExit(event: KeyboardEvent): void {\n if (!(event instanceof KeyboardEvent)) return;\n if (event.key !== \"Tab\") return;\n\n const isFocusedContainer = event.target === this.card;\n const isFocusedAnchor = event.target === this.overlay.anchor;\n const isFocusedWithin = this.card.contains(event.target as Node);\n\n const stopEvent = () => {\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n\n if (!(isFocusedWithin || isFocusedContainer || isFocusedAnchor)) {\n // If focus is outside the card, return to the card\n stopEvent();\n this.card.focus();\n return;\n }\n\n // Check focusables every time because the card contents may have changed\n // but exclude the full screen enter button from this list of elements\n const focusableElements = getAllFocusableChildren(this.card).filter(\n (el) => !el.classList.contains(Card.attr.CLASS_FULL_SCREEN_ENTER)\n );\n const hasFocusableElements = focusableElements.length > 0;\n\n // We need to handle five cases:\n // 1. The card has no focusable elements --> focus the anchor\n // 2. Focus is on the card container (do nothing, natural tab order)\n // 3. Focus is on the anchor and the user pressed Tab + Shift (backwards)\n // -> Move to the last focusable element (end of card)\n // 4. Focus is on the last focusable element and the user pressed Tab\n // (forwards) -> Move to the anchor (top of card)\n // 5. otherwise we don't interfere\n\n if (!hasFocusableElements) {\n // case 1\n stopEvent();\n this.overlay.anchor.focus();\n return;\n }\n\n // case 2\n if (isFocusedContainer) return;\n\n const lastFocusable = focusableElements[focusableElements.length - 1];\n const isFocusedLast = event.target === lastFocusable;\n\n if (isFocusedAnchor && event.shiftKey) {\n stopEvent();\n lastFocusable.focus();\n return;\n }\n\n if (isFocusedLast && !event.shiftKey) {\n stopEvent();\n this.overlay.anchor.focus();\n return;\n }\n }\n\n /**\n * Creates the full screen overlay.\n * @private\n * @returns {CardFullScreenOverlay}\n */\n private _createOverlay(): CardFullScreenOverlay {\n const container = document.createElement(\"div\");\n container.id = Card.attr.ID_FULL_SCREEN_OVERLAY;\n container.onclick = this.exitFullScreen.bind(this);\n\n const anchor = this._createOverlayCloseAnchor();\n container.appendChild(anchor);\n\n return { container, anchor };\n }\n\n /**\n * Creates the anchor element used to exit the full screen mode.\n * @private\n * @returns {CardFullScreenOverlay[\"anchor\"]}\n */\n private _createOverlayCloseAnchor(): CardFullScreenOverlay[\"anchor\"] {\n const anchor = document.createElement(\"a\");\n anchor.classList.add(Card.attr.CLASS_FULL_SCREEN_EXIT);\n anchor.tabIndex = 0;\n anchor.setAttribute(\"aria-expanded\", \"true\");\n anchor.setAttribute(\"aria-label\", \"Close card\");\n anchor.setAttribute(\"role\", \"button\");\n anchor.onclick = (ev) => {\n this.exitFullScreen();\n ev.stopPropagation();\n };\n anchor.onkeydown = (ev) => {\n if (ev.key === \"Enter\" || ev.key === \" \") {\n this.exitFullScreen();\n }\n };\n anchor.innerHTML = this._overlayCloseHtml();\n\n return anchor;\n }\n\n /**\n * Returns the HTML for the close icon.\n * @private\n * @returns {string}\n */\n private _overlayCloseHtml(): string {\n return (\n \"Close \" +\n \"\" +\n \"\"\n );\n }\n\n /**\n * The registry of card instances and their associated DOM elements.\n * @private\n * @static\n * @type {WeakMap}\n */\n private static instanceMap: WeakMap = new WeakMap();\n\n /**\n * Returns the card instance associated with the given element, if any.\n * @public\n * @static\n * @param {HTMLElement} el\n * @returns {(Card | undefined)}\n */\n public static getInstance(el: HTMLElement): Card | undefined {\n return Card.instanceMap.get(el);\n }\n\n /**\n * If cards are initialized before the DOM is ready, we re-schedule the\n * initialization to occur on DOMContentLoaded.\n * @private\n * @static\n * @type {boolean}\n */\n private static onReadyScheduled = false;\n\n /**\n * Initializes all cards that require initialization on the page, or schedules\n * initialization if the DOM is not yet ready.\n * @public\n * @static\n * @param {boolean} [flushResizeObserver=true]\n */\n public static initializeAllCards(flushResizeObserver = true): void {\n if (document.readyState === \"loading\") {\n if (!Card.onReadyScheduled) {\n Card.onReadyScheduled = true;\n document.addEventListener(\"DOMContentLoaded\", () => {\n Card.initializeAllCards(false);\n });\n }\n return;\n }\n\n if (flushResizeObserver) {\n // Trigger a recheck of observed cards to unobserve non-existent cards\n Card.shinyResizeObserver.flush();\n }\n\n const initSelector = `.${Card.attr.CLASS_CARD}[${Card.attr.ATTR_INIT}]`;\n if (!document.querySelector(initSelector)) {\n // no cards to initialize\n return;\n }\n\n const cards = document.querySelectorAll(initSelector);\n cards.forEach((card) => new Card(card as HTMLElement));\n }\n}\n\n// attach Sidebar class to window for global usage\n(window as any).bslib = (window as any).bslib || {};\n(window as any).bslib.Card = Card;\n\nexport { Card };\n", "import { InputBinding, registerBinding } from \"./_utils\";\nimport { ShinyResizeObserver } from \"./_shinyResizeObserver\";\n\n/**\n * Methods for programmatically toggling the state of the sidebar. These methods\n * describe the desired state of the sidebar: `\"close\"` and `\"open\"` transition\n * the sidebar to the desired state, unless the sidebar is already in that\n * state. `\"toggle\"` transitions the sidebar to the state opposite of its\n * current state.\n * @typedef {SidebarToggleMethod}\n */\ntype SidebarToggleMethod = \"close\" | \"open\" | \"toggle\";\n\n/**\n * Data received by the input binding's `receiveMessage` method.\n * @typedef {SidebarMessageData}\n */\ntype SidebarMessageData = {\n method: SidebarToggleMethod;\n};\n\n/**\n * Represents the size of the sidebar window either: \"desktop\" or \"mobile\".\n */\ntype SidebarWindowSize = \"desktop\" | \"mobile\";\n\n/**\n * The DOM elements that make up the sidebar. `main`, `sidebar`, and `toggle`\n * are all direct children of `container` (in that order).\n * @interface SidebarComponents\n * @typedef {SidebarComponents}\n */\ninterface SidebarComponents {\n /**\n * The `layout_sidebar()` parent container, with class\n * `Sidebar.classes.LAYOUT`.\n * @type {HTMLElement}\n */\n container: HTMLElement;\n /**\n * The main content area of the sidebar layout.\n * @type {HTMLElement}\n */\n main: HTMLElement;\n /**\n * The sidebar container of the sidebar layout.\n * @type {HTMLElement}\n */\n sidebar: HTMLElement;\n /**\n * The toggle button that is used to toggle the sidebar state.\n * @type {HTMLElement}\n */\n toggle: HTMLElement;\n}\n\n/**\n * The bslib sidebar component class. This class is only used for collapsible\n * sidebars.\n *\n * @class Sidebar\n * @typedef {Sidebar}\n */\nclass Sidebar {\n /**\n * The DOM elements that make up the sidebar, see `SidebarComponents`.\n * @private\n * @type {SidebarComponents}\n */\n private layout: SidebarComponents;\n\n /**\n * A Shiny-specific resize observer that ensures Shiny outputs in the main\n * content areas of the sidebar resize appropriately.\n * @private\n * @type {ShinyResizeObserver}\n * @static\n */\n private static shinyResizeObserver = new ShinyResizeObserver();\n\n /**\n * Creates an instance of a collapsible bslib Sidebar.\n * @constructor\n * @param {HTMLElement} container\n */\n constructor(container: HTMLElement) {\n Sidebar.instanceMap.set(container, this);\n this.layout = {\n container,\n main: container.querySelector(\":scope > .main\") as HTMLElement,\n sidebar: container.querySelector(\":scope > .sidebar\") as HTMLElement,\n toggle: container.querySelector(\n \":scope > .collapse-toggle\"\n ) as HTMLElement,\n } as SidebarComponents;\n\n const sideAccordion = this.layout.sidebar.querySelector(\n \":scope > .sidebar-content > .accordion\"\n );\n if (sideAccordion) {\n // Add `.has-accordion` class to `.sidebar-content` container\n sideAccordion?.parentElement?.classList.add(\"has-accordion\");\n sideAccordion.classList.add(\"accordion-flush\");\n }\n\n this._initSidebarCounters();\n this._initSidebarState();\n\n if (this._isCollapsible(\"desktop\") || this._isCollapsible(\"mobile\")) {\n this._initEventListeners();\n }\n\n // Start watching the main content area for size changes to ensure Shiny\n // outputs resize appropriately during sidebar transitions.\n Sidebar.shinyResizeObserver.observe(this.layout.main);\n\n container.removeAttribute(\"data-bslib-sidebar-init\");\n const initScript = container.querySelector(\n \":scope > script[data-bslib-sidebar-init]\"\n );\n if (initScript) {\n container.removeChild(initScript);\n }\n }\n\n /**\n * Read the current state of the sidebar. Note that, when calling this method,\n * the sidebar may be transitioning into the state returned by this method.\n *\n * @description\n * The sidebar state works as follows, starting from the open state. When the\n * sidebar is closed:\n * 1. We add both the `COLLAPSE` and `TRANSITIONING` classes to the sidebar.\n * 2. The sidebar collapse begins to animate. In general, where it is\n * supported, we transition the `grid-template-columns` property of the\n * sidebar layout. We also rotate the collapse icon and we use this\n * rotation to determine when the transition is complete.\n * 3. If another sidebar state toggle is requested while closing the sidebar,\n * we remove the `COLLAPSE` class and the animation immediately starts to\n * reverse.\n * 4. When the `transition` is complete, we remove the `TRANSITIONING` class.\n * @readonly\n * @type {boolean}\n */\n get isClosed(): boolean {\n return this.layout.container.classList.contains(Sidebar.classes.COLLAPSE);\n }\n\n /**\n * Static classes related to the sidebar layout or state.\n * @public\n * @static\n * @readonly\n * @type {{ LAYOUT: string; COLLAPSE: string; TRANSITIONING: string; }}\n */\n public static readonly classes = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n LAYOUT: \"bslib-sidebar-layout\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n COLLAPSE: \"sidebar-collapsed\",\n // eslint-disable-next-line @typescript-eslint/naming-convention\n TRANSITIONING: \"transitioning\",\n };\n\n /**\n * If sidebars are initialized before the DOM is ready, we re-schedule the\n * initialization to occur on DOMContentLoaded.\n * @private\n * @static\n * @type {boolean}\n */\n private static onReadyScheduled = false;\n /**\n * A map of initialized sidebars to their respective Sidebar instances.\n * @private\n * @static\n * @type {WeakMap}\n */\n private static instanceMap: WeakMap = new WeakMap();\n\n /**\n * Given a sidebar container, return the Sidebar instance associated with it.\n * @public\n * @static\n * @param {HTMLElement} el\n * @returns {(Sidebar | undefined)}\n */\n public static getInstance(el: HTMLElement): Sidebar | undefined {\n return Sidebar.instanceMap.get(el);\n }\n\n /**\n * Determine whether the sidebar is collapsible at a given screen size.\n * @private\n * @param {SidebarWindowSize} [size=\"desktop\"]\n * @returns {boolean}\n */\n private _isCollapsible(size: SidebarWindowSize = \"desktop\"): boolean {\n const { container } = this.layout;\n\n const attr =\n size === \"desktop\" ? \"collapsibleDesktop\" : \"collapsibleMobile\";\n\n const isCollapsible = container.dataset[attr];\n\n if (isCollapsible === undefined) {\n return true;\n }\n\n return isCollapsible.trim().toLowerCase() !== \"false\";\n }\n\n /**\n * Initialize all collapsible sidebars on the page.\n * @public\n * @static\n * @param {boolean} [flushResizeObserver=true] When `true`, we remove\n * non-existent elements from the ResizeObserver. This is required\n * periodically to prevent memory leaks. To avoid over-checking, we only flush\n * the ResizeObserver when initializing sidebars after page load.\n */\n public static initCollapsibleAll(flushResizeObserver = true): void {\n if (document.readyState === \"loading\") {\n if (!Sidebar.onReadyScheduled) {\n Sidebar.onReadyScheduled = true;\n document.addEventListener(\"DOMContentLoaded\", () => {\n Sidebar.initCollapsibleAll(false);\n });\n }\n return;\n }\n\n const initSelector = `.${Sidebar.classes.LAYOUT}[data-bslib-sidebar-init]`;\n if (!document.querySelector(initSelector)) {\n // no sidebars to initialize\n return;\n }\n\n if (flushResizeObserver) Sidebar.shinyResizeObserver.flush();\n\n const containers = document.querySelectorAll(initSelector);\n containers.forEach((container) => new Sidebar(container as HTMLElement));\n }\n\n /**\n * Initialize event listeners for the sidebar toggle button.\n * @private\n */\n private _initEventListeners(): void {\n const { toggle } = this.layout;\n\n toggle.addEventListener(\"click\", (ev) => {\n ev.preventDefault();\n this.toggle(\"toggle\");\n });\n\n // Remove the transitioning class when the transition ends. We watch the\n // collapse toggle icon because it's guaranteed to transition, whereas not\n // all browsers support animating grid-template-columns.\n toggle\n .querySelector(\".collapse-icon\")\n ?.addEventListener(\"transitionend\", () => this._finalizeState());\n\n if (this._isCollapsible(\"desktop\") && this._isCollapsible(\"mobile\")) {\n return;\n }\n\n // The sidebar is *sometimes* collapsible, so we need to handle window\n // resize events to ensure visibility and expected behavior.\n window.addEventListener(\"resize\", () => this._handleWindowResizeEvent());\n }\n\n /**\n * Initialize nested sidebar counters.\n *\n * @description\n * This function walks up the DOM tree, adding CSS variables to each direct\n * parent sidebar layout that count the layout's position in the stack of\n * nested layouts. We use these counters to keep the collapse toggles from\n * overlapping. Note that always-open sidebars that don't have collapse\n * toggles break the chain of nesting.\n * @private\n */\n private _initSidebarCounters(): void {\n const { container } = this.layout;\n\n const selectorChildLayouts =\n `.${Sidebar.classes.LAYOUT}` +\n \"> .main > \" +\n `.${Sidebar.classes.LAYOUT}:not([data-bslib-sidebar-open=\"always\"])`;\n\n const isInnermostLayout =\n container.querySelector(selectorChildLayouts) === null;\n\n if (!isInnermostLayout) {\n // There are sidebar layouts nested within this layout; defer to children\n return;\n }\n\n function nextSidebarParent(el: HTMLElement | null): HTMLElement | null {\n el = el ? el.parentElement : null;\n if (el && el.classList.contains(\"main\")) {\n // .bslib-sidebar-layout > .main > .bslib-sidebar-layout\n el = el.parentElement;\n }\n if (el && el.classList.contains(Sidebar.classes.LAYOUT)) {\n return el;\n }\n return null;\n }\n\n const layouts = [container];\n let parent = nextSidebarParent(container);\n\n while (parent) {\n // Add parent to front of layouts array, so we sort outer -> inner\n layouts.unshift(parent);\n parent = nextSidebarParent(parent);\n }\n\n const count = { left: 0, right: 0 };\n layouts.forEach(function (x: HTMLElement): void {\n const isRight = x.classList.contains(\"sidebar-right\");\n const thisCount = isRight ? count.right++ : count.left++;\n x.style.setProperty(\"--_js-toggle-count-this-side\", thisCount.toString());\n x.style.setProperty(\n \"--_js-toggle-count-max-side\",\n Math.max(count.right, count.left).toString()\n );\n });\n }\n\n /**\n * Retrieves the current window size by reading a CSS variable whose value is\n * toggled via media queries.\n * @returns The window size as `\"desktop\"` or `\"mobile\"`, or `\"\"` if not\n * available.\n */\n private _getWindowSize(): SidebarWindowSize | \"\" {\n const { container } = this.layout;\n\n return window\n .getComputedStyle(container)\n .getPropertyValue(\"--bslib-sidebar-js-window-size\")\n .trim() as SidebarWindowSize | \"\";\n }\n\n /**\n * Determine the initial toggle state of the sidebar at a current screen size.\n * It always returns whether we should `\"open\"` or `\"close\"` the sidebar.\n *\n * @private\n * @returns {(\"close\" | \"open\")}\n */\n private _initialToggleState(): \"close\" | \"open\" {\n const { container } = this.layout;\n\n const attr = this.windowSize === \"desktop\" ? \"openDesktop\" : \"openMobile\";\n\n const initState = container.dataset[attr]?.trim()?.toLowerCase();\n\n if (initState === undefined) {\n return \"open\";\n }\n\n if ([\"open\", \"always\"].includes(initState)) {\n return \"open\";\n }\n\n if ([\"close\", \"closed\"].includes(initState)) {\n return \"close\";\n }\n\n return \"open\";\n }\n\n /**\n * Initialize the sidebar's initial state when `open = \"desktop\"`.\n * @private\n */\n private _initSidebarState(): void {\n // Check the CSS variable to find out which mode we're in right now\n this.windowSize = this._getWindowSize();\n\n const initState = this._initialToggleState();\n this.toggle(initState, true);\n }\n\n /**\n * The current window size, either `\"desktop\"` or `\"mobile\"`.\n * @private\n * @type {SidebarWindowSize | \"\"}\n */\n private windowSize: SidebarWindowSize | \"\" = \"\";\n\n /**\n * Updates the sidebar state when the window is resized across the mobile-\n * desktop boundary.\n */\n private _handleWindowResizeEvent(): void {\n const newSize = this._getWindowSize();\n if (!newSize || newSize == this.windowSize) {\n return;\n }\n\n // Re-initializing for the new size also updates the tracked window size\n this._initSidebarState();\n }\n\n /**\n * Toggle the sidebar's open/closed state.\n * @public\n * @param {SidebarToggleMethod | undefined} method Whether to `\"open\"`,\n * `\"close\"` or `\"toggle\"` the sidebar. If `.toggle()` is called without an\n * argument, it will toggle the sidebar's state.\n * @param {boolean} [immediate=false] If `true`, the sidebar state will be\n * set immediately, without a transition. This is primarily used when the\n * sidebar is initialized.\n */\n public toggle(\n method: SidebarToggleMethod | undefined,\n immediate = false\n ): void {\n if (typeof method === \"undefined\") {\n method = \"toggle\";\n }\n\n const { container, sidebar } = this.layout;\n const isClosed = this.isClosed;\n\n if ([\"open\", \"close\", \"toggle\"].indexOf(method) === -1) {\n throw new Error(`Unknown method ${method}`);\n }\n\n if (method === \"toggle\") {\n method = isClosed ? \"open\" : \"close\";\n }\n\n if ((isClosed && method === \"close\") || (!isClosed && method === \"open\")) {\n // nothing to do, sidebar is already in the desired state\n if (immediate) this._finalizeState();\n return;\n }\n\n if (method === \"open\") {\n // unhide sidebar immediately when opening,\n // otherwise the sidebar is hidden on transitionend\n sidebar.hidden = false;\n }\n\n // If not immediate, add the .transitioning class to the sidebar for smooth\n // transitions. This class is removed when the transition ends.\n container.classList.toggle(Sidebar.classes.TRANSITIONING, !immediate);\n container.classList.toggle(Sidebar.classes.COLLAPSE);\n\n if (immediate) {\n // When transitioning, state is finalized on transitionend, otherwise we\n // need to manually and immediately finalize the state.\n this._finalizeState();\n }\n }\n\n /**\n * When the sidebar open/close transition ends, finalize the sidebar's state.\n * @private\n */\n private _finalizeState(): void {\n const { container, sidebar, toggle } = this.layout;\n container.classList.remove(Sidebar.classes.TRANSITIONING);\n sidebar.hidden = this.isClosed;\n toggle.setAttribute(\"aria-expanded\", this.isClosed ? \"false\" : \"true\");\n\n // Send browser-native event with updated sidebar state\n const event = new CustomEvent(\"bslib.sidebar\", {\n bubbles: true,\n detail: { open: !this.isClosed },\n });\n sidebar.dispatchEvent(event);\n\n // Trigger Shiny input and output binding events\n $(sidebar).trigger(\"toggleCollapse.sidebarInputBinding\");\n $(sidebar).trigger(this.isClosed ? \"hidden\" : \"shown\");\n }\n}\n\n/**\n * A Shiny input binding for a sidebar.\n * @class SidebarInputBinding\n * @typedef {SidebarInputBinding}\n * @extends {InputBinding}\n */\nclass SidebarInputBinding extends InputBinding {\n find(scope: HTMLElement) {\n return $(scope).find(`.${Sidebar.classes.LAYOUT} > .bslib-sidebar-input`);\n }\n\n getValue(el: HTMLElement): boolean {\n const sb = Sidebar.getInstance(el.parentElement as HTMLElement);\n if (!sb) return false;\n return !sb.isClosed;\n }\n\n setValue(el: HTMLElement, value: boolean): void {\n const method = value ? \"open\" : \"close\";\n this.receiveMessage(el, { method });\n }\n\n subscribe(el: HTMLElement, callback: (x: boolean) => void) {\n $(el).on(\n \"toggleCollapse.sidebarInputBinding\",\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n function (event) {\n callback(true);\n }\n );\n }\n\n unsubscribe(el: HTMLElement) {\n $(el).off(\".sidebarInputBinding\");\n }\n\n receiveMessage(el: HTMLElement, data: SidebarMessageData) {\n const sb = Sidebar.getInstance(el.parentElement as HTMLElement);\n if (sb) sb.toggle(data.method);\n }\n}\n\nregisterBinding(SidebarInputBinding, \"sidebar\");\n\n// attach Sidebar class to window for global usage\n(window as any).bslib = (window as any).bslib || {};\n(window as any).bslib.Sidebar = Sidebar;\n", "import { InputBinding, registerBinding } from \"./_utils\";\nimport type { BslibSwitchInline } from \"./webcomponents/switch\";\n\ntype TaskButtonMessage = {\n state: string;\n};\n\n/**\n * This is a Shiny input binding for `bslib::input_task_button()`. It is not a\n * web component, though one of its children is . The\n * reason it is not a web component is because it is primarily a button, and I\n * wanted to use the native