diff --git a/.Rbuildignore b/.Rbuildignore index 1fa2852..44b1be9 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -8,3 +8,4 @@ ^LICENSE\.md$ ^.vscode$ ^index\.md$ +^README\.Rmd$ diff --git a/DESCRIPTION b/DESCRIPTION index 6522f45..01aa5e1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,3 +22,5 @@ Imports: shiny, magrittr, httpuv +Depends: + R (>= 2.10) diff --git a/R/sysdata.rda b/R/sysdata.rda new file mode 100644 index 0000000..d7c4acf Binary files /dev/null and b/R/sysdata.rda differ diff --git a/R/utils-shiny.R b/R/utils-shiny.R index 0ff6f9d..e57187e 100644 --- a/R/utils-shiny.R +++ b/R/utils-shiny.R @@ -468,3 +468,16 @@ buildTabItem <- function (index, tabsetId, foundSelected, tabs = NULL, divTag = } return(list(liTag = liTag, divTag = divTag)) } + +create_link_iframe <- function(link) { + shiny::tags$iframe( + class = "html-fill-item", + src = link, + height = "800", + width = "100%", + style = "border: 1px solid rgba(0,0,0,0.175); border-radius: .375rem;", + allowfullscreen = "", + allow = "autoplay", + `data-external` = "1" + ) +} \ No newline at end of file diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 0000000..3e5709b --- /dev/null +++ b/README.Rmd @@ -0,0 +1,44 @@ +--- +output: github_document +--- + + + +# shiny386 + + +[![CRAN status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) +[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +[![R build status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) + + +The goal of shiny386 is to provide an old school Bootstrap 4 template for Shiny. It is built on top of the [Bootstrap 386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML template. + +## Installation + +You can install the released version of shiny386 from Github with: + +``` r +pak::paks("RinteRface/shiny386") +``` + +## Example + +This is a basic example which shows you how to solve a common problem: + +``` r +library(shiny) +library(shiny386) +ui <- page_386( + card_386( + title = "My card", + "This is my card", + br(), + card_link_386(href = "https://www.google.com", "More"), + footer = "Card footer" + ) +) + +server <- function(input, output, session) {} +shinyApp(ui, server) +``` diff --git a/README.md b/README.md index 2acd21c..91cfe03 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,29 @@ + + # shiny386 -[![CRAN status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) -[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -[![R build status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) + +[![CRAN +status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) +[![Lifecycle: +experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +[![R build +status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) -The goal of shiny386 is to provide an old school Bootstrap 4 template for Shiny. It is built on top of the [Bootstrap 386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML template. +The goal of shiny386 is to provide an old school Bootstrap 4 template +for Shiny. It is built on top of the [Bootstrap +386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML +template. ## Installation You can install the released version of shiny386 from Github with: ``` r -remotes::install_github("RinteRface/shiny386") +pak::paks("RinteRface/shiny386") ``` ## Example @@ -37,4 +46,3 @@ ui <- page_386( server <- function(input, output, session) {} shinyApp(ui, server) ``` - diff --git a/_pkgdown.yml b/_pkgdown.yml index e69de29..604e5ea 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -0,0 +1,3 @@ +template: + bootstrap: 5 +url: https://shiny386.rinterface.com/ diff --git a/index.Rmd b/index.Rmd new file mode 100644 index 0000000..a93b577 --- /dev/null +++ b/index.Rmd @@ -0,0 +1,60 @@ +--- +output: github_document +always_allow_html: true +--- + +```{r setup, include=FALSE} +library(bslib) +knitr::opts_chunk$set(echo = TRUE) +``` + +# shiny386 + + +[![CRAN status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) +[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +[![R build status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) + + +The goal of shiny386 is to provide an old school Bootstrap 4 template for Shiny. It is built on top of the [Bootstrap 386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML template. + +## Installation + +You can install the released version of shiny386 from Github with: + +``` r +pak::pak("RinteRface/shiny386") +``` + +## Demo + +```{r showcase-code, eval=TRUE, echo=FALSE} +card( + shiny386:::create_link_iframe(shiny386:::shinylive_links["inst/examples/shinylive/basic"]), + full_screen = TRUE, + style = "margin: 0 auto; float: none;" +) +``` + +The original app may be found [here](https://shiny.rstudio.com/gallery/bus-dashboard.html) + +## Example + +This is a basic example which shows you how to solve a common problem: + +``` r +library(shiny) +library(shiny386) +ui <- page_386( + card_386( + title = "My card", + "This is my card", + br(), + card_link_386(href = "https://www.google.com", "More"), + footer = "Card footer" + ) +) + +server <- function(input, output, session) {} +shinyApp(ui, server) +``` \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..f6f6839 --- /dev/null +++ b/index.html @@ -0,0 +1,649 @@ + + + + + + + + + + + + + + + + + + + +

shiny386

+ + +

CRAN status Lifecycle: experimental R build status

+ + +

The goal of shiny386 is to provide an old school Bootstrap 4 template +for Shiny. It is built on top of the Bootstrap +386 HTML template.

+

Installation

+

You can install the released version of shiny386 from Github +with:

+
pak::pak("RinteRface/shiny386")
+

Demo

+
+
+ +
+ + + + + +
+ +

The original app may be found here

+

Example

+

This is a basic example which shows you how to solve a common +problem:

+
library(shiny)
+library(shiny386)
+ui <- page_386(
+ card_386(
+  title = "My card",
+  "This is my card",
+  br(),
+  card_link_386(href = "https://www.google.com", "More"),
+  footer = "Card footer"
+ )
+)
+
+server <- function(input, output, session) {}
+shinyApp(ui, server)
+ + + diff --git a/index.md b/index.md index 79f3b22..77163a7 100644 --- a/index.md +++ b/index.md @@ -2,26 +2,43 @@ # shiny386 -[![CRAN status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) -[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -[![R build status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) + +[![CRAN +status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) +[![Lifecycle: +experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +[![R build +status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) -The goal of shiny386 is to provide an old school Bootstrap 4 template for Shiny. It is built on top of the [Bootstrap 386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML template. +The goal of shiny386 is to provide an old school Bootstrap 4 template +for Shiny. It is built on top of the [Bootstrap +386](http://kristopolous.github.io/BOOTSTRA.386/demo.html) HTML +template. ## Installation You can install the released version of shiny386 from Github with: ``` r -remotes::install_github("RinteRface/shiny386") +pak::pak("RinteRface/shiny386") ``` ## Demo - - -The original app may be found [here](https://shiny.rstudio.com/gallery/bus-dashboard.html) +
+
+ +
+ + + + + +
+ +The original app may be found +[here](https://shiny.rstudio.com/gallery/bus-dashboard.html) ## Example diff --git a/inst/examples/shinylive/basic/app.R b/inst/examples/shinylive/basic/app.R new file mode 100644 index 0000000..8e38a8a --- /dev/null +++ b/inst/examples/shinylive/basic/app.R @@ -0,0 +1,19 @@ +webr::install( + "shiny386", + repos = c("https://rinterface.github.io/rinterface-wasm-cran/", "https://repo.r-wasm.org") +) + +library(shiny) +library(shiny386) +ui <- page_386( + card_386( + title = "My card", + "This is my card", + br(), + card_link_386(href = "https://www.google.com", "More"), + footer = "Card footer" + ) +) + +server <- function(input, output, session) {} +shinyApp(ui, server) \ No newline at end of file diff --git a/inst/shinylive/tools.R b/inst/shinylive/tools.R new file mode 100644 index 0000000..d7b6b8f --- /dev/null +++ b/inst/shinylive/tools.R @@ -0,0 +1,19 @@ +pak::pak("parmsam/r-shinylive@feat/encode-decode-url") + +create_shinylive_links <- function(path) { + + dirs <- list.dirs(path)[-1] + + vapply( + list.dirs(path)[-1], + shinylive:::url_encode_dir, + FUN.VALUE = character(1) + ) +} + +create_vignettes_links <- function() { + create_shinylive_links("inst/examples/shinylive") +} + +shinylive_links <- create_vignettes_links() +usethis::use_data(shinylive_links, internal = TRUE, overwrite = TRUE) \ No newline at end of file diff --git a/pkgdown/extra.css b/pkgdown/extra.css new file mode 100644 index 0000000..2a7e185 --- /dev/null +++ b/pkgdown/extra.css @@ -0,0 +1,23 @@ +/* bslib cards */ +.bslib-card{overflow:auto}.bslib-card .card-body+.card-body{padding-top:0}.bslib-card .card-body{overflow:auto}.bslib-card .card-body p{margin-top:0}.bslib-card .card-body p:last-child{margin-bottom:0}.bslib-card .card-body{max-height:var(--bslib-card-body-max-height, none)}.bslib-card[data-full-screen="true"]>.card-body{max-height:var(--bslib-card-body-max-height-full-screen, none)}.bslib-card .card-header .form-group{margin-bottom:0}.bslib-card .card-header .selectize-control{margin-bottom:0}.bslib-card .card-header .selectize-control .item{margin-right:1.15rem}.bslib-card .card-footer{margin-top:auto}.bslib-card .bslib-navs-card-title{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.bslib-card .bslib-navs-card-title .nav{margin-left:auto}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border="true"]){border:none}.bslib-card .bslib-sidebar-layout:not([data-bslib-sidebar-border-radius="true"]){border-top-left-radius:0;border-top-right-radius:0}[data-full-screen="true"]{position:fixed;inset:3.5rem 1rem 1rem;height:auto !important;max-height:none !important;width:auto !important;z-index:1070}.bslib-full-screen-enter{display:none;position:absolute;bottom:var(--bslib-full-screen-enter-bottom, 0.2rem);right:var(--bslib-full-screen-enter-right, 0);top:var(--bslib-full-screen-enter-top);left:var(--bslib-full-screen-enter-left);color:var(--bslib-color-fg, var(--bs-card-color));background-color:var(--bslib-color-bg, var(--bs-card-bg, var(--bs-body-bg)));border:var(--bs-card-border-width) solid var(--bslib-color-fg, var(--bs-card-border-color));box-shadow:0 2px 4px rgba(0,0,0,0.15);margin:0.2rem 0.4rem;padding:0.55rem !important;font-size:.8rem;cursor:pointer;opacity:0.7;z-index:1070}.bslib-full-screen-enter:hover{opacity:1}.card[data-full-screen="false"]:hover>*>.bslib-full-screen-enter{display:block}.bslib-has-full-screen .card:hover>*>.bslib-full-screen-enter{display:none}@media (max-width: 575.98px){.bslib-full-screen-enter{display:none !important}}.bslib-full-screen-exit{position:relative;top:1.35rem;font-size:0.9rem;cursor:pointer;text-decoration:none;display:flex;float:right;margin-right:2.15rem;align-items:center;color:rgba(var(--bs-body-bg-rgb), 0.8)}.bslib-full-screen-exit:hover{color:rgba(var(--bs-body-bg-rgb), 1)}.bslib-full-screen-exit svg{margin-left:0.5rem;font-size:1.5rem}#bslib-full-screen-overlay{position:fixed;inset:0;background-color:rgba(var(--bs-body-color-rgb), 0.6);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);z-index:1069;animation:bslib-full-screen-overlay-enter 400ms cubic-bezier(0.6, 0.02, 0.65, 1) forwards}@keyframes bslib-full-screen-overlay-enter{0%{opacity:0}100%{opacity:1}} + +/* fill css for fullscreen cards */ +.html-fill-container { + display: flex; + flex-direction: column; + /* Prevent the container from expanding vertically or horizontally beyond its + parent's constraints. */ + min-height: 0; + min-width: 0; +} +.html-fill-container > .html-fill-item { + /* Fill items can grow and shrink freely within + available vertical space in fillable container */ + flex: 1 1 auto; + min-height: 0; + min-width: 0; +} +.html-fill-container > :not(.html-fill-item) { + /* Prevent shrinking or growing of non-fill items */ + flex: 0 0 auto; +} \ No newline at end of file diff --git a/pkgdown/extra.js b/pkgdown/extra.js new file mode 100644 index 0000000..879ceb6 --- /dev/null +++ b/pkgdown/extra.js @@ -0,0 +1,804 @@ +/*! bslib 0.8.0 | (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 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, s) => { + var n = o => { + try { + d(t.next(o)) + } catch (b) { + s(b) + } + } + , l = o => { + try { + d(t.throw(o)) + } catch (b) { + s(b) + } + } + , d = o => o.done ? i(o.value) : Promise.resolve(o.value).then(n, 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(n => n + t.join("")) + , s = r.querySelectorAll(i.join(", ")); + return Array.from(s) + } + 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(s => s.isOpen()).map(s => s.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) + , s = this._getValues(e, i, t.values); + i.forEach(n => { + s.indexOf(n.value) > -1 ? n.show() : n.hide() + } + ) + } + _openItems(e, t) { + let i = this._getItemInfo(e) + , s = this._getValues(e, i, t.values); + i.forEach(n => { + s.indexOf(n.value) > -1 && n.show() + } + ) + } + _closeItems(e, t) { + let i = this._getItemInfo(e) + , s = this._getValues(e, i, t.values); + i.forEach(n => { + s.indexOf(n.value) > -1 && n.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 s = t.panel; + if (i ? yield E(i, s, t.position === "before" ? "beforeBegin" : "afterEnd") : yield E(e, s), + this._isAutoClosing(e)) { + let n = $(s.html).attr("data-value"); + $(e).find(`[data-value="${n}"] .accordion-collapse`).attr("data-bs-parent", "#" + e.id) + } + }) + } + _removeItem(e, t) { + var n; + let i = this._getItemInfo(e).filter(l => t.target.indexOf(l.value) > -1) + , s = (n = window.Shiny) == null ? void 0 : n.unbindAll; + i.forEach(l => { + s && s(l.item), + l.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 n = i.querySelector(".accordion-body"); + yield E(n, t.body) + } + let s = i.querySelector(".accordion-header"); + if (w(t, "title")) { + let n = s.querySelector(".accordion-title"); + yield E(n, t.title) + } + if (w(t, "icon")) { + let n = s.querySelector(".accordion-button > .accordion-icon"); + yield E(n, 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 s = i !== !0 ? i : t.map(l => l.value); + return this._isAutoClosing(e) && (s = s.slice(s.length - 1, s.length)), + s + } + _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 s of e) + s.target instanceof HTMLElement && s.target.querySelector(".shiny-bound-output") && s.target.querySelectorAll(".shiny-bound-output").forEach(n => { + if (i.includes(n)) + return; + let {binding: l, onResize: d} = $(n).data("shinyOutputBinding"); + if (!l || !l.resize) + return; + let o = n.shinyResizeObserver; + if (o && o !== this || (o || (n.shinyResizeObserver = this), + d(n), + i.push(n), + !n.classList.contains("shiny-plot-output"))) + return; + let b = n.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 s = new Set; + for (let {type: n, removedNodes: l} of i) + if (n === "childList" && l.length !== 0) + for (let d of l) + d instanceof HTMLElement && (d.matches(e) && s.add(d), + d.querySelector(e) && d.querySelectorAll(e).forEach(o => s.add(o))); + if (s.size !== 0) + for (let n of s) + try { + t(n) + } 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 + , s = this.card.contains(e.target) + , n = () => { + e.preventDefault(), + e.stopImmediatePropagation() + } + ; + if (!(s || t || i)) { + n(), + this.card.focus(); + return + } + let l = U(this.card).filter(A => !A.classList.contains(a.attr.CLASS_FULL_SCREEN_ENTER)); + if (!(l.length > 0)) { + n(), + this.overlay.anchor.focus(); + return + } + if (t) + return; + let o = l[l.length - 1] + , b = e.target === o; + if (i && e.shiftKey) { + n(), + o.focus(); + return + } + if (b && !e.shiftKey) { + n(), + 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(s => new a(s)) + } + } + , + 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 s; + 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 && ((s = t == null ? void 0 : t.parentElement) == null || s.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" + , s = t.dataset[i]; + return s === void 0 ? !0 : s.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(s => new c(s)) + } + _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 s(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 n = [e] + , l = s(e); + for (; l; ) + n.unshift(l), + l = s(l); + let d = { + left: 0, + right: 0 + }; + n.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 s, n; + let {container: e} = this.layout + , t = this.windowSize === "desktop" ? "openDesktop" : "openMobile" + , i = (n = (s = e.dataset[t]) == null ? void 0 : s.trim()) == null ? void 0 : n.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: s} = this.layout + , n = this.isClosed; + if (["open", "close", "toggle"].indexOf(e) === -1) + throw new Error(`Unknown method ${e}`); + if (e === "toggle" && (e = n ? "open" : "close"), + n && e === "close" || !n && e === "open") { + t && this._finalizeState(); + return + } + e === "open" && (s.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 s = new CustomEvent("bslib.sidebar",{ + bubbles: !0, + detail: { + open: !this.isClosed + } + }); + t.dispatchEvent(s), + $(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 s = () => { + var n; + v(this, T).set(t, ((n = v(this, T).get(t)) != null ? n : 0) + 1), + i(!0), + O(this, C, x).call(this, t, "busy") + } + ; + v(this, M).set(t, s), + t.addEventListener("click", s) + } + unsubscribe(t) { + let i = v(this, M).get(t); + i && t.removeEventListener("click", i) + } + receiveMessage(s, n) { + 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 s = t.querySelector("bslib-switch-inline"); + s && (s.case = i) + } + ; + y(N, "task-button") + } + ); + function V(r) { + if (window.Shiny) + for (let[e,t] of Object.entries(r)) + window.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" ? G() : document.addEventListener("DOMContentLoaded", G) + } + ); + Z(); +} +)(); +//# sourceMappingURL=components.min.js.map