├── .github
├── .gitignore
└── workflows
│ └── R-CMD-check.yaml
├── srcjs
├── config
│ ├── misc.json
│ ├── output_path.json
│ ├── entry_points.json
│ ├── externals.json
│ └── loaders.json
├── index.js
└── exts
│ └── WinBox.js
├── LICENSE
├── .gitignore
├── man
├── figures
│ └── winbox.png
├── html_dependency_winbox.Rd
├── wbControls.Rd
├── WinBox.Rd
└── wbOptions.Rd
├── webpack.prod.js
├── webpack.dev.js
├── .Rbuildignore
├── R
├── utils.R
└── WinBox.R
├── inst
├── examples
│ ├── basic.R
│ ├── controls.R
│ ├── default.R
│ ├── htmlwidgets2.R
│ ├── ggplot.R
│ ├── modal.R
│ ├── htmlwidgets.R
│ ├── options.R
│ ├── close.R
│ └── options2.R
└── packer
│ └── WinBox.js
├── NAMESPACE
├── DESCRIPTION
├── package.json
├── LICENSE.md
├── README.md
└── webpack.common.js
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/srcjs/config/misc.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/srcjs/index.js:
--------------------------------------------------------------------------------
1 | import './exts/WinBox.js';
2 |
--------------------------------------------------------------------------------
/srcjs/config/output_path.json:
--------------------------------------------------------------------------------
1 | "./inst/packer"
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2022
2 | COPYRIGHT HOLDER: Victor PERRIER
3 |
--------------------------------------------------------------------------------
/srcjs/config/entry_points.json:
--------------------------------------------------------------------------------
1 | {
2 | "WinBox": "./srcjs/exts/WinBox.js"
3 | }
4 |
--------------------------------------------------------------------------------
/srcjs/config/externals.json:
--------------------------------------------------------------------------------
1 | {
2 | "shiny": "Shiny",
3 | "jquery": "jQuery"
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | .Ruserdata
5 | node_modules
6 | *.Rproj
7 |
--------------------------------------------------------------------------------
/man/figures/winbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamRs/shinywb/HEAD/man/figures/winbox.png
--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'production',
6 | });
7 |
--------------------------------------------------------------------------------
/srcjs/config/loaders.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "test": "\\.css$",
4 | "use": [
5 | "style-loader",
6 | "css-loader"
7 | ]
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = merge(common, {
5 | mode: 'development',
6 | devtool: 'inline-source-map'
7 | });
8 |
--------------------------------------------------------------------------------
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^shinywb\.Rproj$
2 | ^\.Rproj\.user$
3 | ^srcjs$
4 | ^node_modules$
5 | ^package\.json$
6 | ^package-lock\.json$
7 | ^webpack\.dev\.js$
8 | ^webpack\.prod\.js$
9 | ^webpack\.common\.js$
10 | ^LICENSE\.md$
11 | ^\.github$
12 |
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 |
2 | genId <- function(bytes = 12) {
3 | paste(format(as.hexmode(sample(256, bytes, replace = TRUE) - 1), width = 2), collapse = "")
4 | }
5 |
6 | dropNulls <- function(x) {
7 | x[!vapply(x, is.null, FUN.VALUE = logical(1))]
8 | }
9 |
10 | list1 <- function(x) {
11 | if (length(x) == 1) {
12 | list(x)
13 | }
14 | else {
15 | x
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/inst/examples/basic.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | html_dependency_winbox()
7 | )
8 |
9 | server <- function(input, output, session) {
10 |
11 | WinBox(
12 | title = "WinBox",
13 | ui = tagList(
14 | tags$h3("Hello from WinBox!")
15 | )
16 | )
17 |
18 | }
19 |
20 | if (interactive())
21 | shinyApp(ui, server)
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | export(WinBox)
4 | export(closeWinBox)
5 | export(html_dependency_winbox)
6 | export(wbControls)
7 | export(wbOptions)
8 | importFrom(htmltools,css)
9 | importFrom(htmltools,doRenderTags)
10 | importFrom(htmltools,htmlDependency)
11 | importFrom(htmltools,tags)
12 | importFrom(shiny,getDefaultReactiveDomain)
13 | importFrom(utils,packageVersion)
14 |
--------------------------------------------------------------------------------
/inst/examples/controls.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | html_dependency_winbox(),
7 | actionButton(inputId = "show", label = "Show WinBox")
8 | )
9 |
10 | server <- function(input, output, session) {
11 |
12 | observeEvent(input$show, {
13 | WinBox(
14 | title = "Custom controls",
15 | ui = tagList(
16 | tags$h2("Hello from WinBox!"),
17 | "Text content of winbox."
18 | ),
19 | controls = wbControls(
20 | min = FALSE,
21 | max = FALSE,
22 | resize = FALSE
23 | )
24 | )
25 | })
26 |
27 | }
28 |
29 | if (interactive())
30 | shinyApp(ui, server)
31 |
--------------------------------------------------------------------------------
/inst/examples/default.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | html_dependency_winbox(),
7 | actionButton(inputId = "show", label = "Show WinBox"),
8 | verbatimTextOutput("res")
9 | )
10 |
11 | server <- function(input, output, session) {
12 |
13 | observeEvent(input$show, {
14 | WinBox(
15 | title = "WinBox window",
16 | ui = tagList(
17 | tags$h2("Hello from WinBox!"),
18 | "Text content of winbox.",
19 | selectInput("month", "Select a month:", month.name)
20 | )
21 | )
22 | })
23 |
24 | output$res <- renderPrint(input$month)
25 |
26 | }
27 |
28 | if (interactive())
29 | shinyApp(ui, server)
30 |
--------------------------------------------------------------------------------
/inst/examples/htmlwidgets2.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 | library(reactable)
5 | library(ggplot2)
6 | data("midwest", package = "ggplot2")
7 |
8 | ui <- fluidPage(
9 | html_dependency_winbox(),
10 | actionButton(inputId = "show", label = "Show WinBox")
11 | )
12 |
13 | server <- function(input, output, session) {
14 |
15 | observeEvent(input$show, {
16 | inputId <- paste0("var", input$show)
17 | WinBox(
18 | title = "With an htmlwidget",
19 | ui = tagList(
20 | tags$h3("Midwest demographics"),
21 | renderReactable({
22 | reactable(data = midwest, bordered = TRUE, striped = TRUE)
23 | })
24 | ),
25 | options = wbOptions(height = 630)
26 | )
27 | })
28 |
29 | }
30 |
31 | if (interactive())
32 | shinyApp(ui, server)
33 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: shinywb
2 | Title: 'WinBox' Window for 'shiny' Applications
3 | Version: 0.0.0.9200
4 | Authors@R: c(
5 | person("Victor", "Perrier", , "victor.perrier@dreamrs.fr", role = c("aut", "cre")),
6 | person("Fanny", "Meyer", role = "aut"),
7 | person(given = "Thomas",
8 | family = "Wilkerling",
9 | role = "cph",
10 | comment = "WinBox JavaScript library ")
11 | )
12 | Description: Interface to 'WinBox' 'JavaScript' library to use in 'shiny' applications.
13 | 'WinBox' is a modern HTML5 window manager for the web, .
14 | License: MIT + file LICENSE
15 | Encoding: UTF-8
16 | Roxygen: list(markdown = TRUE)
17 | RoxygenNote: 7.3.1
18 | Imports:
19 | htmltools,
20 | shiny
21 |
--------------------------------------------------------------------------------
/inst/examples/ggplot.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 | library(ggplot2)
5 | data("economics", package = "ggplot2")
6 |
7 | ui <- fluidPage(
8 | html_dependency_winbox(),
9 | actionButton(inputId = "show", label = "Show WinBox")
10 | )
11 |
12 | server <- function(input, output, session) {
13 |
14 | observeEvent(input$show, {
15 | inputId <- paste0("var", input$show)
16 | WinBox(
17 | title = "With ggplot2",
18 | ui = tagList(
19 | tags$h3("Economic chart"),
20 | selectInput(inputId, "Select a variable:", names(economics)[-1]),
21 | renderPlot({
22 | ggplot(economics) +
23 | aes(x = date, y = !!sym(input[[inputId]])) +
24 | geom_line()
25 | })
26 | ),
27 | options = wbOptions(height = 630)
28 | )
29 | })
30 |
31 | }
32 |
33 | if (interactive())
34 | shinyApp(ui, server)
35 |
--------------------------------------------------------------------------------
/inst/examples/modal.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | html_dependency_winbox(),
7 | actionButton(inputId = "show", label = "Show WinBox as modal")
8 | )
9 |
10 | server <- function(input, output, session) {
11 |
12 |
13 | observeEvent(input$show, {
14 | WinBox(
15 | title = "WinBox as modal",
16 | ui = tagList(
17 | tags$h2("Hello from WinBox!"),
18 | "Text content of winbox.",
19 | actionButton("show2", "Open a normal winbox")
20 | ),
21 | options = wbOptions(modal = TRUE)
22 | )
23 | })
24 |
25 | observeEvent(input$show2, {
26 | WinBox(
27 | title = "Normal WinBox",
28 | ui = tagList(
29 | tags$h2("Hello from WinBox!"),
30 | "Text content of winbox."
31 | ),
32 | options = wbOptions(background = "firebrick")
33 | )
34 | })
35 |
36 | }
37 |
38 | if (interactive())
39 | shinyApp(ui, server)
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "winboxr",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "none": "webpack --config webpack.dev.js --mode=none",
9 | "development": "webpack --config webpack.dev.js",
10 | "production": "webpack --config webpack.prod.js",
11 | "watch": "webpack --config webpack.dev.js -d --watch"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/pvictor/winboxr.git"
16 | },
17 | "keywords": [],
18 | "author": "",
19 | "license": "ISC",
20 | "bugs": {
21 | "url": "https://github.com/pvictor/winboxr/issues"
22 | },
23 | "homepage": "https://github.com/pvictor/winboxr#readme",
24 | "devDependencies": {
25 | "css-loader": "^6.7.1",
26 | "style-loader": "^3.3.1",
27 | "webpack": "^5.72.0",
28 | "webpack-cli": "^4.9.2",
29 | "webpack-merge": "^5.8.0",
30 | "winbox": "^0.2.82"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/man/html_dependency_winbox.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/WinBox.R
3 | \name{html_dependency_winbox}
4 | \alias{html_dependency_winbox}
5 | \title{Winbox JavaScript Dependencies}
6 | \usage{
7 | html_dependency_winbox(
8 | css_rules = "body{min-height:100vh}.winbox.modal{display:block;overflow:unset}"
9 | )
10 | }
11 | \arguments{
12 | \item{css_rules}{CSS rules to be included in a \code{style} tag in the document head.
13 | By default it set a \code{min-height} to the body element.}
14 | }
15 | \description{
16 | Include dependencies, place anywhere in the shiny UI.
17 | }
18 | \examples{
19 |
20 | library(shiny)
21 | library(shinywb)
22 |
23 | ui <- fluidPage(
24 | html_dependency_winbox()
25 | )
26 |
27 | server <- function(input, output, session) {
28 |
29 | WinBox(
30 | title = "WinBox",
31 | ui = tagList(
32 | tags$h3("Hello from WinBox!")
33 | )
34 | )
35 |
36 | }
37 |
38 | if (interactive())
39 | shinyApp(ui, server)
40 | }
41 |
--------------------------------------------------------------------------------
/inst/examples/htmlwidgets.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 | library(apexcharter)
5 | library(ggplot2)
6 | data("economics", package = "ggplot2")
7 |
8 | ui <- fluidPage(
9 | html_dependency_winbox(),
10 | actionButton(inputId = "show", label = "Show WinBox")
11 | )
12 |
13 | server <- function(input, output, session) {
14 |
15 | observeEvent(input$show, {
16 | inputId <- paste0("var", input$show)
17 | WinBox(
18 | title = "With an htmlwidget",
19 | ui = tagList(
20 | tags$h3("Economic chart"),
21 | selectInput(inputId, "Select a variable:", names(economics)[-1]),
22 | renderApexchart({
23 | apex(
24 | data = economics,
25 | type = "line",
26 | mapping = aes(x = date, y = !!sym(input[[inputId]]))
27 | ) %>%
28 | ax_stroke(width = 1)
29 | })
30 | ),
31 | options = wbOptions(height = 630)
32 | )
33 | })
34 |
35 | }
36 |
37 | if (interactive())
38 | shinyApp(ui, server)
39 |
--------------------------------------------------------------------------------
/inst/examples/options.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | html_dependency_winbox(),
7 | actionButton(inputId = "show1", label = "Show WinBox"),
8 | actionButton(inputId = "show2", label = "Show WinBox as modal")
9 | )
10 |
11 | server <- function(input, output, session) {
12 |
13 | observeEvent(input$show1, {
14 | WinBox(
15 | title = "Custom background color and border",
16 | ui = tagList(
17 | tags$h2("Hello from WinBox!"),
18 | "Text content of winbox."
19 | ),
20 | options = wbOptions(
21 | background = "#112446",
22 | border = "0.5em",
23 | x = "center",
24 | y = "center",
25 | width = "50%",
26 | height = "50%"
27 | )
28 | )
29 | })
30 |
31 | observeEvent(input$show2, {
32 | WinBox(
33 | title = "WinBox as modal",
34 | ui = tagList(
35 | tags$h2("Hello from WinBox!"),
36 | "Text content of winbox."
37 | ),
38 | options = wbOptions(modal = TRUE)
39 | )
40 | })
41 |
42 | }
43 |
44 | if (interactive())
45 | shinyApp(ui, server)
46 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) 2022 Victor PERRIER
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/inst/examples/close.R:
--------------------------------------------------------------------------------
1 |
2 | library(shiny)
3 | library(shinywb)
4 |
5 | ui <- fluidPage(
6 | tags$style("body {min-height: 100vh;}"),
7 | html_dependency_winbox(),
8 | tags$p("With an ID:"),
9 | actionButton(inputId = "show", label = "Show WinBox with ID"),
10 | actionButton(inputId = "close", label = "Close WinBox with ID"),
11 | tags$p("Without ID, close last one:"),
12 | actionButton(inputId = "show_mult", label = "Show multiple WinBox"),
13 | actionButton(inputId = "close_last", label = "Close last WinBox")
14 | )
15 |
16 | server <- function(input, output, session) {
17 |
18 | observeEvent(input$show, {
19 | WinBox(
20 | id = "mywinbox",
21 | title = "WinBox window",
22 | ui = tags$div(
23 | style = "padding: 10px;",
24 | tags$h2("Hello from WinBox!"),
25 | "Text content of winbox."
26 | )
27 | )
28 | })
29 |
30 | observeEvent(input$close, closeWinBox("mywinbox"))
31 |
32 | observeEvent(input$show_mult, {
33 | WinBox(
34 | title = paste("WinBox window", input$show_mult),
35 | ui = tags$div(
36 | style = "padding: 10px;",
37 | tags$h2("Hello from WinBox!"),
38 | "Text content of winbox."
39 | )
40 | )
41 | })
42 | observeEvent(input$close_last, closeWinBox(NULL))
43 |
44 | }
45 |
46 | shinyApp(ui, server)
47 |
--------------------------------------------------------------------------------
/inst/examples/options2.R:
--------------------------------------------------------------------------------
1 | library(shiny)
2 | library(shinywb)
3 | library(htmltools)
4 | library(phosphoricons)
5 |
6 |
7 | ui <- fluidPage(
8 | html_dependency_winbox(),
9 | actionButton(inputId = "show", label = "Show one or more WinBox"),
10 | radioButtons(
11 | inputId = "background",
12 | label = "Background color:",
13 | choices = c("forestgreen", "firebrick", "steelblue", "goldenrod")
14 | ),
15 | checkboxInput(
16 | inputId = "modal",
17 | label = "Shows the window as modal",
18 | value = FALSE
19 | ),
20 | checkboxInput(
21 | inputId = "autosize",
22 | label = "Automatically size the window to fit the window contents.",
23 | value = FALSE
24 | ),
25 | checkboxInput(
26 | inputId = "max",
27 | label = "Automatically toggles the window into maximized state when created.",
28 | value = FALSE
29 | ),
30 | checkboxInput(
31 | inputId = "min",
32 | label = "Automatically toggles the window into minimized state when created.",
33 | value = FALSE
34 | )
35 | )
36 |
37 | server <- function(input, output, session) {
38 |
39 | observeEvent(input$show, {
40 | WinBox(
41 | title = "This is a WinBox",
42 | ui = tagList(
43 | tags$h3("Hello from WinBox!")
44 | ),
45 | options = wbOptions(
46 | background = input$background,
47 | modal = input$modal,
48 | autosize = input$autosize,
49 | max = input$max,
50 | min = input$min
51 | )
52 | )
53 | })
54 |
55 | }
56 |
57 | if (interactive())
58 | shinyApp(ui, server)
59 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: R-CMD-check
10 |
11 | jobs:
12 | R-CMD-check:
13 | runs-on: ${{ matrix.config.os }}
14 |
15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | config:
21 | - {os: macos-latest, r: 'release'}
22 | - {os: windows-latest, r: 'release'}
23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
24 | - {os: ubuntu-latest, r: 'release'}
25 | - {os: ubuntu-latest, r: 'oldrel-1'}
26 |
27 | env:
28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
29 | R_KEEP_PKG_SOURCE: yes
30 |
31 | steps:
32 | - uses: actions/checkout@v4
33 |
34 | - uses: r-lib/actions/setup-pandoc@v2
35 |
36 | - uses: r-lib/actions/setup-r@v2
37 | with:
38 | r-version: ${{ matrix.config.r }}
39 | http-user-agent: ${{ matrix.config.http-user-agent }}
40 | use-public-rspm: true
41 |
42 | - uses: r-lib/actions/setup-r-dependencies@v2
43 | with:
44 | extra-packages: any::rcmdcheck
45 | needs: check
46 |
47 | - uses: r-lib/actions/check-r-package@v2
48 | with:
49 | upload-snapshots: true
50 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
51 |
--------------------------------------------------------------------------------
/srcjs/exts/WinBox.js:
--------------------------------------------------------------------------------
1 | import $ from "jquery";
2 | import "shiny";
3 | import WinBox from "winbox/src/js/winbox";
4 | import "winbox/dist/css/winbox.min.css";
5 |
6 | let winboxes = {};
7 |
8 | Shiny.addCustomMessageHandler("WinBox-show", msg => {
9 | var options = msg.options;
10 | options.html = `
`;
11 | options.onclose = function() {
12 | Shiny.unbindAll($content);
13 | delete winboxes[options.id];
14 | };
15 | options.onresize = function(width, height) {
16 | $("#shiny-winbox-" + options.id)
17 | .find(".html-widget, .shiny-plot-output")
18 | .trigger("resize");
19 | };
20 | if (winboxes.hasOwnProperty(options.id)) {
21 | winboxes[options.id].close();
22 | }
23 | var winbox = new WinBox(options);
24 | var $content = $("#shiny-winbox-" + options.id);
25 | Shiny.renderContent($content, { html: msg.html, deps: msg.deps });
26 | if (!options.hasOwnProperty("height") && msg.auto_height) {
27 | setTimeout(function() {
28 | winbox.height = $content.height() + 45;
29 | winbox.resize();
30 | }, 100);
31 | }
32 | //winbox.body.innerHTML = msg.html;
33 | winboxes[winbox.id] = winbox;
34 | });
35 |
36 | Shiny.addCustomMessageHandler("WinBox-close", msg => {
37 | if (msg.hasOwnProperty("id")) {
38 | if (winboxes.hasOwnProperty(msg.id)) {
39 | winboxes[msg.id].close();
40 | delete winboxes[msg.id];
41 | }
42 | } else {
43 | var keys = Object.keys(winboxes);
44 | var last = keys.length - 1;
45 | if (last > -1) {
46 | winboxes[keys[last]].close();
47 | delete winboxes[keys[last]];
48 | }
49 | }
50 | });
51 |
52 |
--------------------------------------------------------------------------------
/man/wbControls.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/WinBox.R
3 | \name{wbControls}
4 | \alias{wbControls}
5 | \title{WinBox controls}
6 | \usage{
7 | wbControls(
8 | animation = TRUE,
9 | shadow = TRUE,
10 | header = TRUE,
11 | min = TRUE,
12 | max = TRUE,
13 | full = FALSE,
14 | close = TRUE,
15 | resize = TRUE,
16 | move = TRUE
17 | )
18 | }
19 | \arguments{
20 | \item{animation}{If \code{FALSE}, disables the windows transition animation.}
21 |
22 | \item{shadow}{If \code{FALSE}, disables the windows drop shadow.}
23 |
24 | \item{header}{If \code{FALSE}, hide the window header incl. title and toolbar.}
25 |
26 | \item{min}{If \code{FALSE}, hide the minimize icon.}
27 |
28 | \item{max}{If \code{FALSE}, hide the maximize icon.}
29 |
30 | \item{full}{If \code{FALSE}, hide the fullscreen icon.}
31 |
32 | \item{close}{If \code{FALSE}, hide the close icon.}
33 |
34 | \item{resize}{If \code{FALSE}, disables the window resizing capability.}
35 |
36 | \item{move}{If \code{FALSE}, disables the window moving capability.}
37 | }
38 | \value{
39 | A \code{list} of controls to use in \code{\link[=WinBox]{WinBox()}}.
40 | }
41 | \description{
42 | WinBox controls
43 | }
44 | \examples{
45 |
46 | library(shiny)
47 | library(shinywb)
48 |
49 | ui <- fluidPage(
50 | html_dependency_winbox(),
51 | actionButton(inputId = "show", label = "Show WinBox")
52 | )
53 |
54 | server <- function(input, output, session) {
55 |
56 | observeEvent(input$show, {
57 | WinBox(
58 | title = "Custom controls",
59 | ui = tagList(
60 | tags$h2("Hello from WinBox!"),
61 | "Text content of winbox."
62 | ),
63 | controls = wbControls(
64 | min = FALSE,
65 | max = FALSE,
66 | resize = FALSE
67 | )
68 | )
69 | })
70 |
71 | }
72 |
73 | if (interactive())
74 | shinyApp(ui, server)
75 | }
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # shinywb
2 |
3 |
4 | [](https://lifecycle.r-lib.org/articles/stages.html#experimental)
5 | [](https://github.com/dreamRs/shinywb/actions/workflows/R-CMD-check.yaml)
6 |
7 |
8 | > Interface to [WinBox](https://nextapps-de.github.io/winbox/) JavaScript library to use in [shiny](https://shiny.rstudio.com/) applications. WinBox is a modern HTML5 window manager for the web.
9 |
10 | ## Installation
11 |
12 | You can install the development version of shinywb from [GitHub](https://github.com/) with:
13 |
14 | ``` r
15 | # install.packages("remotes")
16 | remotes::install_github("dreamRs/shinywb")
17 | ```
18 |
19 | ## Example
20 |
21 | Create window from your `server` function with `WinBox()` :
22 |
23 | ```r
24 | library(shiny)
25 | library(shinywb)
26 | library(apexcharter)
27 | library(ggplot2)
28 | data("economics", package = "ggplot2")
29 |
30 | ui <- fluidPage(
31 | html_dependency_winbox(),
32 | actionButton(inputId = "show", label = "Show WinBox")
33 | )
34 |
35 | server <- function(input, output, session) {
36 |
37 | observeEvent(input$show, {
38 | inputId <- paste0("var", input$show)
39 | WinBox(
40 | title = "With an htmlwidget",
41 | ui = tagList(
42 | tags$h3("Economic chart"),
43 | selectInput(inputId, "Select a variable:", names(economics)[-1]),
44 | renderApexchart({
45 | apex(
46 | data = economics,
47 | type = "line",
48 | mapping = aes(x = date, y = !!sym(input[[inputId]]))
49 | ) %>%
50 | ax_stroke(width = 1)
51 | })
52 | ),
53 | options = wbOptions(height = 630)
54 | )
55 | })
56 |
57 | }
58 |
59 | if (interactive())
60 | shinyApp(ui, server)
61 | ```
62 |
63 | 
64 |
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 |
4 | // defaults
5 | var outputPath = [],
6 | entryPoints = [],
7 | externals = [],
8 | misc = [],
9 | loaders = [];
10 |
11 | var outputPathFile = './srcjs/config/output_path.json',
12 | entryPointsFile = './srcjs/config/entry_points.json',
13 | externalsFile = './srcjs/config/externals.json',
14 | miscFile = './srcjs/config/misc.json',
15 | loadersFile = './srcjs/config/loaders.json';
16 |
17 | // Read config files
18 | if(fs.existsSync(outputPathFile)){
19 | outputPath = fs.readFileSync(outputPathFile, 'utf8');
20 | }
21 |
22 | if(fs.existsSync(entryPointsFile)){
23 | entryPoints = fs.readFileSync(entryPointsFile, 'utf8');
24 | }
25 |
26 | if(fs.existsSync(externalsFile)){
27 | externals = fs.readFileSync(externalsFile, 'utf8');
28 | }
29 |
30 | if(fs.existsSync(miscFile)){
31 | misc = fs.readFileSync(miscFile, 'utf8');
32 | }
33 |
34 | if(fs.existsSync(loadersFile)){
35 | loaders = fs.readFileSync(loadersFile, 'utf8');
36 | }
37 |
38 | if(fs.existsSync(loadersFile)){
39 | loaders = fs.readFileSync(loadersFile, 'utf8');
40 | }
41 |
42 | // parse
43 | loaders = JSON.parse(loaders);
44 | misc = JSON.parse(misc);
45 | externals = JSON.parse(externals);
46 | entryPoints = JSON.parse(entryPoints);
47 |
48 | // parse regex
49 | loaders.forEach((loader) => {
50 | loader.test = RegExp(loader.test);
51 | return(loader);
52 | })
53 |
54 | // placeholder for plugins
55 | var plugins = [
56 | ];
57 |
58 | // define options
59 | var options = {
60 | entry: entryPoints,
61 | output: {
62 | filename: '[name].js',
63 | path: path.resolve(__dirname, JSON.parse(outputPath)),
64 | },
65 | externals: externals,
66 | module: {
67 | rules: loaders
68 | },
69 | resolve: {
70 | extensions: ['.tsx', '.ts', '.js'],
71 | },
72 | plugins: plugins
73 | };
74 |
75 | // add misc
76 | if(misc.resolve)
77 | options.resolve = misc.resolve;
78 |
79 | // export
80 | module.exports = options;
81 |
--------------------------------------------------------------------------------
/man/WinBox.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/WinBox.R
3 | \name{WinBox}
4 | \alias{WinBox}
5 | \alias{closeWinBox}
6 | \title{WinBox}
7 | \usage{
8 | WinBox(
9 | title,
10 | ui,
11 | options = wbOptions(),
12 | controls = wbControls(),
13 | id = NULL,
14 | padding = "5px 10px",
15 | auto_height = FALSE,
16 | session = shiny::getDefaultReactiveDomain()
17 | )
18 |
19 | closeWinBox(id, session = shiny::getDefaultReactiveDomain())
20 | }
21 | \arguments{
22 | \item{title}{Title for the window.}
23 |
24 | \item{ui}{Content of the window.}
25 |
26 | \item{options}{List of options, see \code{\link[=wbOptions]{wbOptions()}}.}
27 |
28 | \item{controls}{List of controls, see \code{\link[=wbControls]{wbControls()}}.}
29 |
30 | \item{id}{An unique identifier for the window, if a window with the same \code{id} is already open,
31 | it will be closed before opening the new one. When closing windows, use \code{id = NULL} to close last one opened.}
32 |
33 | \item{padding}{Padding for the window content.}
34 |
35 | \item{auto_height}{Automatically set height of the window according to content.
36 | Note that if content does not have a fix height it may not work properly.}
37 |
38 | \item{session}{Shiny session.}
39 | }
40 | \value{
41 | No value, a window is openned in the UI.
42 | }
43 | \description{
44 | A window manager with JavaScript library \href{https://nextapps-de.github.io/winbox/}{WinBox.js}.
45 | }
46 | \note{
47 | You need to include \code{\link[=html_dependency_winbox]{html_dependency_winbox()}} in your UI definition for this function to work.
48 | }
49 | \examples{
50 |
51 | library(shiny)
52 | library(shinywb)
53 |
54 | ui <- fluidPage(
55 | html_dependency_winbox(),
56 | actionButton(inputId = "show", label = "Show WinBox"),
57 | verbatimTextOutput("res")
58 | )
59 |
60 | server <- function(input, output, session) {
61 |
62 | observeEvent(input$show, {
63 | WinBox(
64 | title = "WinBox window",
65 | ui = tagList(
66 | tags$h2("Hello from WinBox!"),
67 | "Text content of winbox.",
68 | selectInput("month", "Select a month:", month.name)
69 | )
70 | )
71 | })
72 |
73 | output$res <- renderPrint(input$month)
74 |
75 | }
76 |
77 | if (interactive())
78 | shinyApp(ui, server)
79 | }
80 |
--------------------------------------------------------------------------------
/man/wbOptions.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/WinBox.R
3 | \name{wbOptions}
4 | \alias{wbOptions}
5 | \title{WinBox Options}
6 | \usage{
7 | wbOptions(
8 | width = NULL,
9 | height = NULL,
10 | minwidth = NULL,
11 | minheight = NULL,
12 | x = NULL,
13 | y = NULL,
14 | max = NULL,
15 | min = NULL,
16 | top = NULL,
17 | right = NULL,
18 | bottom = NULL,
19 | left = NULL,
20 | background = NULL,
21 | border = NULL,
22 | modal = NULL,
23 | index = 1045,
24 | ...
25 | )
26 | }
27 | \arguments{
28 | \item{width, height}{Set the initial width/height of the window (supports units "px" and "\%").}
29 |
30 | \item{minwidth, minheight}{Set the minimal width/height of the window (supports units "px" and "\%").}
31 |
32 | \item{x, y}{Set the initial position of the window (supports: "right" for x-axis, "bottom" for y-axis,
33 | "center" for both, units "px" and "\%" for both).}
34 |
35 | \item{max, min}{Automatically toggles the window into maximized / minimized state when created.}
36 |
37 | \item{top, right, bottom, left}{Set or limit the viewport of the window's available area (supports units "px" and "\%").}
38 |
39 | \item{background}{Set the background of the window (supports all CSS styles which are also supported by the style-attribute "background",
40 | e.g. colors, transparent colors, hsl, gradients, background images).}
41 |
42 | \item{border}{Set the border width of the window (supports all css units, like px, \%, em, rem, vh, vmax).}
43 |
44 | \item{modal}{Shows the window as modal.}
45 |
46 | \item{index}{Set the initial z-index of the window to this value (could be increased automatically when unfocused/focused).}
47 |
48 | \item{...}{Other options, see https://github.com/nextapps-de/winbox?tab=readme-ov-file#options.}
49 | }
50 | \value{
51 | A \code{list} of options to use in \code{\link[=WinBox]{WinBox()}}.
52 | }
53 | \description{
54 | WinBox Options
55 | }
56 | \examples{
57 |
58 | library(shiny)
59 | library(shinywb)
60 |
61 | ui <- fluidPage(
62 | html_dependency_winbox(),
63 | actionButton(inputId = "show1", label = "Show WinBox"),
64 | actionButton(inputId = "show2", label = "Show WinBox as modal")
65 | )
66 |
67 | server <- function(input, output, session) {
68 |
69 | observeEvent(input$show1, {
70 | WinBox(
71 | title = "Custom background color and border",
72 | ui = tagList(
73 | tags$h2("Hello from WinBox!"),
74 | "Text content of winbox."
75 | ),
76 | options = wbOptions(
77 | background = "#112446",
78 | border = "0.5em",
79 | x = "center",
80 | y = "center",
81 | width = "50\%",
82 | height = "50\%"
83 | )
84 | )
85 | })
86 |
87 | observeEvent(input$show2, {
88 | WinBox(
89 | title = "WinBox as modal",
90 | ui = tagList(
91 | tags$h2("Hello from WinBox!"),
92 | "Text content of winbox."
93 | ),
94 | options = wbOptions(modal = TRUE)
95 | )
96 | })
97 |
98 | }
99 |
100 | if (interactive())
101 | shinyApp(ui, server)
102 | }
103 |
--------------------------------------------------------------------------------
/R/WinBox.R:
--------------------------------------------------------------------------------
1 |
2 | #' @title Winbox JavaScript Dependencies
3 | #'
4 | #' @description Include dependencies, place anywhere in the shiny UI.
5 | #'
6 | #' @param css_rules CSS rules to be included in a `style` tag in the document head.
7 | #' By default it set a `min-height` to the body element.
8 | #'
9 | #' @importFrom htmltools htmlDependency doRenderTags tags
10 | #' @importFrom utils packageVersion
11 | #'
12 | #' @export
13 | #'
14 | #' @example inst/examples/basic.R
15 | html_dependency_winbox <- function(css_rules = "body{min-height:100vh}.winbox.modal{display:block;overflow:unset}") {
16 | if (!is.null(css_rules)) {
17 | styles <- doRenderTags(tags$style(css_rules))
18 | } else {
19 | styles <- NULL
20 | }
21 | htmlDependency(
22 | name = "winbox",
23 | version = packageVersion("shinywb"),
24 | src = list(file = "packer"),
25 | package = "shinywb",
26 | script = "WinBox.js",
27 | head = styles
28 | )
29 | }
30 |
31 |
32 |
33 | #' @title WinBox
34 | #'
35 | #' @description A window manager with JavaScript library [WinBox.js](https://nextapps-de.github.io/winbox/).
36 | #'
37 | #' @param title Title for the window.
38 | #' @param ui Content of the window.
39 | #' @param options List of options, see [wbOptions()].
40 | #' @param controls List of controls, see [wbControls()].
41 | #' @param id An unique identifier for the window, if a window with the same `id` is already open,
42 | #' it will be closed before opening the new one. When closing windows, use `id = NULL` to close last one opened.
43 | #' @param padding Padding for the window content.
44 | #' @param auto_height Automatically set height of the window according to content.
45 | #' Note that if content does not have a fix height it may not work properly.
46 | #' @param session Shiny session.
47 | #'
48 | #' @return No value, a window is openned in the UI.
49 | #'
50 | #' @note You need to include [html_dependency_winbox()] in your UI definition for this function to work.
51 | #'
52 | #' @name WinBox
53 | #' @export
54 | #'
55 | #' @importFrom shiny getDefaultReactiveDomain
56 | #' @importFrom htmltools tags css
57 | #'
58 | #' @example inst/examples/default.R
59 | WinBox <- function(title,
60 | ui,
61 | options = wbOptions(),
62 | controls = wbControls(),
63 | id = NULL,
64 | padding = "5px 10px",
65 | auto_height = FALSE,
66 | session = shiny::getDefaultReactiveDomain()) {
67 | if (!is.null(padding))
68 | ui <- tags$div(ui, style = css(padding = padding))
69 | res <- utils::getFromNamespace("processDeps", "shiny")(ui, session)
70 | if (is.null(id))
71 | id <- paste0("winbox-", genId())
72 | options$id <- id
73 | options$title <- as.character(title)
74 | options$class <- controls
75 | session$sendCustomMessage("WinBox-show", list(
76 | html = res$html,
77 | deps = res$deps,
78 | options = options,
79 | auto_height = isTRUE(auto_height)
80 | ))
81 | }
82 |
83 | #' @rdname WinBox
84 | #' @export
85 | closeWinBox <- function(id, session = shiny::getDefaultReactiveDomain()) {
86 | session$sendCustomMessage("WinBox-close", dropNulls(list(id = id)))
87 | }
88 |
89 |
90 |
91 | #' WinBox Options
92 | #'
93 | #' @param width,height Set the initial width/height of the window (supports units "px" and "%").
94 | #' @param minwidth,minheight Set the minimal width/height of the window (supports units "px" and "%").
95 | #' @param x,y Set the initial position of the window (supports: "right" for x-axis, "bottom" for y-axis,
96 | #' "center" for both, units "px" and "%" for both).
97 | #' @param max,min Automatically toggles the window into maximized / minimized state when created.
98 | #' @param top,right,bottom,left Set or limit the viewport of the window's available area (supports units "px" and "%").
99 | #' @param background Set the background of the window (supports all CSS styles which are also supported by the style-attribute "background",
100 | #' e.g. colors, transparent colors, hsl, gradients, background images).
101 | #' @param border Set the border width of the window (supports all css units, like px, %, em, rem, vh, vmax).
102 | #' @param modal Shows the window as modal.
103 | #' @param index Set the initial z-index of the window to this value (could be increased automatically when unfocused/focused).
104 | #' @param ... Other options, see https://github.com/nextapps-de/winbox?tab=readme-ov-file#options.
105 | #'
106 | #' @return A `list` of options to use in [WinBox()].
107 | #' @export
108 | #'
109 | #' @example inst/examples/options.R
110 | wbOptions <- function(width = NULL,
111 | height = NULL,
112 | minwidth = NULL,
113 | minheight = NULL,
114 | x = NULL,
115 | y = NULL,
116 | max = NULL,
117 | min = NULL,
118 | top = NULL,
119 | right = NULL,
120 | bottom = NULL,
121 | left = NULL,
122 | background = NULL,
123 | border = NULL,
124 | modal = NULL,
125 | index = 1045,
126 | ...) {
127 | dropNulls(list(
128 | width = width,
129 | height = height,
130 | minwidth = minwidth,
131 | minheight = minheight,
132 | x = x,
133 | y = y,
134 | max = max,
135 | min = min,
136 | top = top,
137 | right = right,
138 | bottom = bottom,
139 | left = left,
140 | background = background,
141 | border = border,
142 | modal = modal,
143 | index = index,
144 | ...
145 | ))
146 | }
147 |
148 |
149 | #' WinBox controls
150 | #'
151 | #' @param animation If `FALSE`, disables the windows transition animation.
152 | #' @param shadow If `FALSE`, disables the windows drop shadow.
153 | #' @param header If `FALSE`, hide the window header incl. title and toolbar.
154 | #' @param min If `FALSE`, hide the minimize icon.
155 | #' @param max If `FALSE`, hide the maximize icon.
156 | #' @param full If `FALSE`, hide the fullscreen icon.
157 | #' @param close If `FALSE`, hide the close icon.
158 | #' @param resize If `FALSE`, disables the window resizing capability.
159 | #' @param move If `FALSE`, disables the window moving capability.
160 | #'
161 | #' @return A `list` of controls to use in [WinBox()].
162 | #' @export
163 | #'
164 | #' @example inst/examples/controls.R
165 | wbControls <- function(animation = TRUE,
166 | shadow = TRUE,
167 | header = TRUE,
168 | min = TRUE,
169 | max = TRUE,
170 | full = FALSE,
171 | close = TRUE,
172 | resize = TRUE,
173 | move = TRUE) {
174 | classes <- c(
175 | animation = animation,
176 | shadow = shadow,
177 | header = header,
178 | min = min,
179 | max = max,
180 | full = full,
181 | close = close,
182 | resize = resize,
183 | move = move
184 | )
185 | classes <- paste0("no-", names(classes)[!unname(classes)])
186 | list1(classes)
187 | }
188 |
--------------------------------------------------------------------------------
/inst/packer/WinBox.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var t={693:(t,i,e)=>{e.d(i,{Z:()=>x});var o=e(81),n=e.n(o),s=e(645),r=e.n(s),h=e(667),a=e.n(h),d=new URL(e(170),e.b),c=new URL(e(920),e.b),l=new URL(e(324),e.b),u=new URL(e(258),e.b),m=r()(n()),w=a()(d),p=a()(c),b=a()(l),f=a()(u);m.push([t.id,"@keyframes wb-fade-in{0%{opacity:0}to{opacity:.85}}.winbox{position:fixed;left:0;top:0;background:#0050ff;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);transition:width .3s,height .3s,left .3s,top .3s;transition-timing-function:cubic-bezier(.3,1,.3,1);contain:layout size;text-align:left;touch-action:none}.wb-body,.wb-header{position:absolute;left:0}.wb-header{top:0;width:100%;height:35px;line-height:35px;color:#fff;overflow:hidden;z-index:1}.wb-body{top:35px;right:0;bottom:0;overflow:auto;-webkit-overflow-scrolling:touch;overflow-scrolling:touch;will-change:contents;background:#fff;margin-top:0!important;contain:strict;z-index:0}.wb-control *,.wb-icon{background-repeat:no-repeat}.wb-drag{height:100%;padding-left:10px;cursor:move}.wb-title{font-family:Arial,sans-serif;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wb-icon{display:none;width:20px;height:100%;margin:-1px 8px 0-3px;float:left;background-size:100%;background-position:center}.wb-e,.wb-w{width:10px;top:0}.wb-n,.wb-s{left:0;height:10px;position:absolute}.wb-n{top:-5px;right:0;cursor:n-resize;z-index:2}.wb-e{position:absolute;right:-5px;bottom:0;cursor:w-resize;z-index:2}.wb-s{bottom:-5px;right:0;cursor:n-resize;z-index:2}.wb-nw,.wb-sw,.wb-w{left:-5px}.wb-w{position:absolute;bottom:0;cursor:w-resize;z-index:2}.wb-ne,.wb-nw,.wb-sw{width:15px;height:15px;z-index:2;position:absolute}.wb-nw{top:-5px;cursor:nw-resize}.wb-ne,.wb-sw{cursor:ne-resize}.wb-ne{top:-5px;right:-5px}.wb-se,.wb-sw{bottom:-5px}.wb-se{position:absolute;right:-5px;width:15px;height:15px;cursor:nw-resize;z-index:2}.wb-control{float:right;height:100%;max-width:100%;text-align:center}.wb-control *{display:inline-block;width:30px;height:100%;max-width:100%;background-position:center;cursor:pointer}.no-close .wb-close,.no-full .wb-full,.no-header .wb-header,.no-max .wb-max,.no-min .wb-min,.no-resize .wb-body~div,.wb-body .wb-hide,.wb-show,.winbox.hide,.winbox.min .wb-body>*,.winbox.min .wb-full,.winbox.min .wb-min,.winbox.modal .wb-full,.winbox.modal .wb-max,.winbox.modal .wb-min{display:none}.winbox.max .wb-drag,.winbox.min .wb-drag{cursor:default}.wb-min{background-image:url("+w+");background-size:14px auto;background-position:center calc(50% + 6px)}.wb-max{background-image:url("+p+");background-size:17px auto}.wb-close{background-image:url("+b+");background-size:15px auto;background-position:5px center}.wb-full{background-image:url("+f+');background-size:16px auto}.winbox.max .wb-body~div,.winbox.min .wb-body~div,.winbox.modal .wb-body~div,.winbox.modal .wb-drag,body.wb-lock iframe{pointer-events:none}.winbox.max{box-shadow:none}.winbox.max .wb-body{margin:0!important}.winbox iframe{position:absolute;width:100%;height:100%;border:0}body.wb-lock .winbox{will-change:left,top,width,height;transition:none}.winbox.modal:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:inherit;border-radius:inherit}.winbox.modal:after{content:"";position:absolute;top:-50vh;left:-50vw;right:-50vw;bottom:-50vh;background:#0d1117;animation:wb-fade-in .2s ease-out forwards;z-index:-1}.no-animation{transition:none}.no-shadow{box-shadow:none}.no-header .wb-body{top:0}.no-move:not(.min) .wb-title{pointer-events:none}.wb-body .wb-show{display:revert}',""]);const x=m},645:t=>{t.exports=function(t){var i=[];return i.toString=function(){return this.map((function(i){var e="",o=void 0!==i[5];return i[4]&&(e+="@supports (".concat(i[4],") {")),i[2]&&(e+="@media ".concat(i[2]," {")),o&&(e+="@layer".concat(i[5].length>0?" ".concat(i[5]):""," {")),e+=t(i),o&&(e+="}"),i[2]&&(e+="}"),i[4]&&(e+="}"),e})).join("")},i.i=function(t,e,o,n,s){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(o)for(var h=0;h0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=s),e&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=e):c[2]=e),n&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=n):c[4]="".concat(n)),i.push(c))}},i}},667:t=>{t.exports=function(t,i){return i||(i={}),t?(t=String(t.__esModule?t.default:t),/^['"].*['"]$/.test(t)&&(t=t.slice(1,-1)),i.hash&&(t+=i.hash),/["'() \t\n]|(%20)/.test(t)||i.needQuotes?'"'.concat(t.replace(/"/g,'\\"').replace(/\n/g,"\\n"),'"'):t):t}},81:t=>{t.exports=function(t){return t[1]}},379:t=>{var i=[];function e(t){for(var e=-1,o=0;o{var i={};t.exports=function(t,e){var o=function(t){if(void 0===i[t]){var e=document.querySelector(t);if(window.HTMLIFrameElement&&e instanceof window.HTMLIFrameElement)try{e=e.contentDocument.head}catch(t){e=null}i[t]=e}return i[t]}(t);if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(e)}},216:t=>{t.exports=function(t){var i=document.createElement("style");return t.setAttributes(i,t.attributes),t.insert(i,t.options),i}},565:(t,i,e)=>{t.exports=function(t){var i=e.nc;i&&t.setAttribute("nonce",i)}},795:t=>{t.exports=function(t){var i=t.insertStyleElement(t);return{update:function(e){!function(t,i,e){var o="";e.supports&&(o+="@supports (".concat(e.supports,") {")),e.media&&(o+="@media ".concat(e.media," {"));var n=void 0!==e.layer;n&&(o+="@layer".concat(e.layer.length>0?" ".concat(e.layer):""," {")),o+=e.css,n&&(o+="}"),e.media&&(o+="}"),e.supports&&(o+="}");var s=e.sourceMap;s&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(s))))," */")),i.styleTagTransform(o,t,i.options)}(i,t,e)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(i)}}}},589:t=>{t.exports=function(t,i){if(i.styleSheet)i.styleSheet.cssText=t;else{for(;i.firstChild;)i.removeChild(i.firstChild);i.appendChild(document.createTextNode(t))}}},920:t=>{t.exports="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHZpZXdCb3g9IjAgMCA5NiA5NiI+PHBhdGggZD0iTTIwIDcxLjMxMUMxNS4zNCA2OS42NyAxMiA2NS4yMyAxMiA2MFYyMGMwLTYuNjMgNS4zNy0xMiAxMi0xMmg0MGM1LjIzIDAgOS42NyAzLjM0IDExLjMxMSA4SDI0Yy0yLjIxIDAtNCAxLjc5LTQgNHY1MS4zMTF6Ii8+PHBhdGggZD0iTTkyIDc2VjM2YzAtNi42My01LjM3LTEyLTEyLTEySDQwYy02LjYzIDAtMTIgNS4zNy0xMiAxMnY0MGMwIDYuNjMgNS4zNyAxMiAxMiAxMmg0MGM2LjYzIDAgMTItNS4zNyAxMi0xMnptLTUyIDRjLTIuMjEgMC00LTEuNzktNC00VjM2YzAtMi4yMSAxLjc5LTQgNC00aDQwYzIuMjEgMCA0IDEuNzkgNCA0djQwYzAgMi4yMS0xLjc5IDQtNCA0SDQweiIvPjwvc3ZnPg=="},258:t=>{t.exports="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjIuNSIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOCAzSDVhMiAyIDAgMCAwLTIgMnYzbTE4IDBWNWEyIDIgMCAwIDAtMi0yaC0zbTAgMThoM2EyIDIgMCAwIDAgMi0ydi0zTTMgMTZ2M2EyIDIgMCAwIDAgMiAyaDMiLz48L3N2Zz4="},324:t=>{t.exports="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xIC0xIDE4IDE4Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJtMS42MTMuMjEuMDk0LjA4M0w4IDYuNTg1IDE0LjI5My4yOTNsLjA5NC0uMDgzYTEgMSAwIDAgMSAxLjQwMyAxLjQwM2wtLjA4My4wOTRMOS40MTUgOGw2LjI5MiA2LjI5M2ExIDEgMCAwIDEtMS4zMiAxLjQ5N2wtLjA5NC0uMDgzTDggOS40MTVsLTYuMjkzIDYuMjkyLS4wOTQuMDgzQTEgMSAwIDAgMSAuMjEgMTQuMzg3bC4wODMtLjA5NEw2LjU4NSA4IC4yOTMgMS43MDdBMSAxIDAgMCAxIDEuNjEzLjIxeiIvPjwvc3ZnPg=="},170:t=>{t.exports="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOCAwaDdhMSAxIDAgMCAxIDAgMkgxYTEgMSAwIDAgMSAwLTJoN3oiLz48L3N2Zz4="}},i={};function e(o){var n=i[o];if(void 0!==n)return n.exports;var s=i[o]={id:o,exports:{}};return t[o](s,s.exports,e),s.exports}e.m=t,e.n=t=>{var i=t&&t.__esModule?()=>t.default:()=>t;return e.d(i,{a:i}),i},e.d=(t,i)=>{for(var o in i)e.o(i,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:i[o]})},e.o=(t,i)=>Object.prototype.hasOwnProperty.call(t,i),e.b=document.baseURI||self.location.href,(()=>{const t=jQuery;var i=e.n(t);Shiny;const o=document.createElement("div");function n(t,i,e,o){t&&t.addEventListener(i,e,o||!1)}function s(t,i,e,o){t&&t.removeEventListener(i,e,o||!1)}function r(t,i){t.stopPropagation(),i&&t.preventDefault()}function h(t,i){return t.getElementsByClassName(i)[0]}function a(t,i){t.classList.add(i)}function d(t,i){t.classList.remove(i)}function c(t,i,e){e=""+e,t["_s_"+i]!==e&&(t.style.setProperty(i,e),t["_s_"+i]=e)}o.innerHTML="";const l=[],u=[],m={capture:!0,passive:!1},w={capture:!0,passive:!0};let p,b,f,x,g,y,v,M=0,z=10;function I(t,i){if(!(this instanceof I))return new I(t);let e,s,a,d,l,m,w,b,A,k,D,T,S,E,Z,H,P,O,Y,B,R,G,Q,U,F,W,_,q,J,V,X,$,K,tt,it,et,ot,nt,st,rt,ht,at,dt,ct;if(p||(p=document.body,p[f="requestFullscreen"]||p[f="msRequestFullscreen"]||p[f="webkitRequestFullscreen"]||p[f="mozRequestFullscreen"]||(f=""),x=f&&f.replace("request","exit").replace("mozRequest","mozCancel").replace("Request","Exit"),n(window,"resize",(function(){j(),L()})),n(p,"mousedown",(function(t){v=!1}),!0),n(p,"mousedown",(function(t){if(!v){const t=u.length;if(t)for(let i=t-1;i>=0;i--){const t=u[i];if(t.focused){t.blur();break}}}})),j()),t&&(i&&(l=t,t=i),"string"==typeof t?l=t:(e=t.id,s=t.index,a=t.root,d=t.template,l=l||t.title,m=t.icon,w=t.mount,b=t.html,A=t.url,k=t.width,D=t.height,T=t.minwidth,S=t.minheight,E=t.maxwidth,Z=t.maxheight,H=t.autosize,P=t.overflow,U=t.min,F=t.max,W=t.hidden,_=t.modal,O=t.x||(_?"center":0),Y=t.y||(_?"center":0),B=t.top,R=t.left,G=t.bottom,Q=t.right,q=t.background,J=t.border,V=t.header,X=t.class,$=t.oncreate,K=t.onclose,tt=t.onfocus,it=t.onblur,et=t.onmove,ot=t.onresize,nt=t.onfullscreen,st=t.onmaximize,rt=t.onminimize,ht=t.onrestore,at=t.onhide,dt=t.onshow,ct=t.onload)),this.dom=function(t){return(t||o).cloneNode(!0)}(d),this.dom.id=this.id=e||"winbox-"+ ++M,this.dom.className="winbox"+(X?" "+("string"==typeof X?X:X.join(" ")):"")+(_?" modal":""),this.dom.winbox=this,this.window=this.dom,this.body=h(this.dom,"wb-body"),this.header=V||35,u.push(this),q&&this.setBackground(q),J?c(this.body,"margin",J+(isNaN(J)?"":"px")):J=0,V){const t=h(this.dom,"wb-header");c(t,"height",V+"px"),c(t,"line-height",V+"px"),c(this.body,"top",V+"px")}l&&this.setTitle(l),m&&this.setIcon(m),w?this.mount(w):b?this.body.innerHTML=b:A&&this.setUrl(A,ct),B=B?C(B,y):0,G=G?C(G,y):0,R=R?C(R,g):0,Q=Q?C(Q,g):0;const lt=g-R-Q,ut=y-B-G;E=E?C(E,lt):lt,Z=Z?C(Z,ut):ut,T=T?C(T,E):150,S=S?C(S,Z):this.header,H?((a||p).appendChild(this.body),k=Math.max(Math.min(this.body.clientWidth+2*J+1,E),T),D=Math.max(Math.min(this.body.clientHeight+this.header+J+1,Z),S),this.dom.appendChild(this.body)):(k=k?C(k,E):0|Math.max(E/2,T),D=D?C(D,Z):0|Math.max(Z/2,S)),O=O?C(O,lt,k):R,Y=Y?C(Y,ut,D):B,this.x=O,this.y=Y,this.width=k,this.height=D,this.minwidth=T,this.minheight=S,this.maxwidth=E,this.maxheight=Z,this.top=B,this.right=Q,this.bottom=G,this.left=R,this.index=s,this.overflow=P,this.min=!1,this.max=!1,this.full=!1,this.hidden=!1,this.focused=!1,this.onclose=K,this.onfocus=tt,this.onblur=it,this.onmove=et,this.onresize=ot,this.onfullscreen=nt,this.onmaximize=st,this.onminimize=rt,this.onrestore=ht,this.onhide=at,this.onshow=dt,W?this.hide():this.focus(),(s||0===s)&&(this.index=s,c(this.dom,"z-index",s),s>z&&(z=s)),F?this.maximize():U?this.minimize():this.resize().move(),function(t){N(t,"drag"),N(t,"n"),N(t,"s"),N(t,"w"),N(t,"e"),N(t,"nw"),N(t,"ne"),N(t,"se"),N(t,"sw"),n(h(t.dom,"wb-min"),"click",(function(i){r(i),t.min?t.restore().focus():t.minimize()})),n(h(t.dom,"wb-max"),"click",(function(i){r(i),t.max?t.restore().focus():t.maximize().focus()})),f?n(h(t.dom,"wb-full"),"click",(function(i){r(i),t.fullscreen().focus()})):t.addClass("no-full"),n(h(t.dom,"wb-close"),"click",(function(i){r(i),t.close()||(t=null)})),n(t.dom,"mousedown",(function(t){v=!0}),!0),n(t.body,"mousedown",(function(i){t.focus()}),!0)}(this),(a||p).appendChild(this.dom),$&&$.call(this,t)}I.new=function(t){return new I(t)},I.stack=function(){return u};const A=I;function C(t,i,e){if("string"==typeof t)if("center"===t)t=(i-e)/2+.5|0;else if("right"===t||"bottom"===t)t=i-e;else{const e=parseFloat(t),o=""+e!==t&&t.substring((""+e).length);t="%"===o?i/100*e+.5|0:e}return t}function k(t){l.splice(l.indexOf(t),1),L(),t.removeClass("min"),t.min=!1,t.dom.title=""}function L(){const t=l.length,i={},e={};for(let o,n,s=0;sg/3*2?g-t.width-t.right:g/2-t.width/2)+h),t.x=Math.max(Math.min(t.x,t.overflow?g-30:g-t.width-t.right),t.overflow?30-t.width:t.left),f=t.x!==m),x&&(t.max&&(t.y=t.top+a),t.y=Math.max(Math.min(t.y,t.overflow?y-t.header:y-t.height-t.bottom),t.top),x=t.y!==w),(f||x)&&(t.max&&t.restore(),t.move()),(p||f)&&(c=n),(b||x)&&(l=s)}function x(t){r(t),d(p,"wb-lock"),o?(s(window,"touchmove",f,w),s(window,"touchend",x,w)):(s(window,"mousemove",f,w),s(window,"mouseup",x,w))}n(e,"mousedown",b,m),n(e,"touchstart",b,m)}function j(){const t=document.documentElement;g=t.clientWidth,y=t.clientHeight}function D(){const t=u.length;if(t)for(let i=t-1;i>=0;i--){const t=u[i];if(!t.min){t.focus();break}}}function T(){if(b.full=!1,document.fullscreen||document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement)return document[x](),!0}I.prototype.mount=function(t){return this.unmount(),t._backstore||(t._backstore=t.parentNode),this.body.textContent="",this.body.appendChild(t),this},I.prototype.unmount=function(t){const i=this.body.firstChild;if(i){const e=t||i._backstore;e&&e.appendChild(i),i._backstore=t}return this},I.prototype.setTitle=function(t){return function(t,i){const e=t.firstChild;e?e.nodeValue=i:t.textContent=i}(h(this.dom,"wb-title"),this.title=t),this},I.prototype.setIcon=function(t){const i=h(this.dom,"wb-icon");return c(i,"background-image","url("+t+")"),c(i,"display","inline-block"),this},I.prototype.setBackground=function(t){return c(this.dom,"background",t),this},I.prototype.setUrl=function(t,i){const e=this.body.firstChild;return e&&"iframe"===e.tagName.toLowerCase()?e.src=t:(this.body.innerHTML='',i&&(this.body.firstChild.onload=i)),this},I.prototype.focus=function(t){if(!1===t)return this.blur();if(!this.focused){const t=u.length;if(t>1)for(let i=1;i<=t;i++){const e=u[t-i];if(e.focused){e.blur(),u.push(u.splice(u.indexOf(this),1)[0]);break}}c(this.dom,"z-index",++z),this.index=z,this.addClass("focus"),this.focused=!0,this.onfocus&&this.onfocus()}return this},I.prototype.blur=function(t){return!1===t?this.focus():(this.focused&&(this.removeClass("focus"),this.focused=!1,this.onblur&&this.onblur()),this)},I.prototype.hide=function(t){return!1===t?this.show():this.hidden?void 0:(this.onhide&&this.onhide(),this.hidden=!0,this.addClass("hide"))},I.prototype.show=function(t){return!1===t?this.hide():this.hidden?(this.onshow&&this.onshow(),this.hidden=!1,this.removeClass("hide")):void 0},I.prototype.minimize=function(t){return!1===t?this.restore():(b&&T(),this.max&&(this.removeClass("max"),this.max=!1),this.min||(l.push(this),L(),this.dom.title=this.title,this.addClass("min"),this.min=!0,this.focused&&(this.blur(),D()),this.onminimize&&this.onminimize()),this)},I.prototype.restore=function(){return b&&T(),this.min&&(k(this),this.resize().move(),this.onrestore&&this.onrestore()),this.max&&(this.max=!1,this.removeClass("max").resize().move(),this.onrestore&&this.onrestore()),this},I.prototype.maximize=function(t){return!1===t?this.restore():(b&&T(),this.min&&k(this),this.max||(this.addClass("max").resize(g-this.left-this.right,y-this.top-this.bottom,!0).move(this.left,this.top,!0),this.max=!0,this.onmaximize&&this.onmaximize()),this)},I.prototype.fullscreen=function(t){if(this.min&&(k(this),this.resize().move()),b&&T()){if(!1===t)return this.restore()}else this.body[f](),b=this,this.full=!0,this.onfullscreen&&this.onfullscreen();return this},I.prototype.close=function(t){if(this.onclose&&this.onclose(t))return!0;this.min&&k(this),u.splice(u.indexOf(this),1),this.unmount(),this.dom.remove(),this.dom.textContent="",this.dom.winbox=null,this.body=null,this.dom=null,this.focused&&D()},I.prototype.move=function(t,i,e){return t||0===t?e||(this.x=t?t=C(t,g-this.left-this.right,this.width):0,this.y=i?i=C(i,y-this.top-this.bottom,this.height):0):(t=this.x,i=this.y),c(this.dom,"left",t+"px"),c(this.dom,"top",i+"px"),this.onmove&&this.onmove(t,i),this},I.prototype.resize=function(t,i,e){return t||0===t?e||(this.width=t?t=C(t,this.maxwidth):0,this.height=i?i=C(i,this.maxheight):0,t=Math.max(t,this.minwidth),i=Math.max(i,this.minheight)):(t=this.width,i=this.height),c(this.dom,"width",t+"px"),c(this.dom,"height",i+"px"),this.onresize&&this.onresize(t,i),this},I.prototype.addControl=function(t){const i=t.class,e=t.image,o=t.click,n=t.index,s=document.createElement("span"),r=h(this.dom,"wb-control"),a=this;return i&&(s.className=i),e&&c(s,"background-image","url("+e+")"),o&&(s.onclick=function(t){o.call(this,t,a)}),r.insertBefore(s,r.childNodes[n||0]),this},I.prototype.removeControl=function(t){return(t=h(this.dom,t))&&t.remove(),this},I.prototype.addClass=function(t){return a(this.dom,t),this},I.prototype.removeClass=function(t){return d(this.dom,t),this},I.prototype.hasClass=function(t){return function(t,i){return t.classList.contains(i)}(this.dom,t)},I.prototype.toggleClass=function(t){return this.hasClass(t)?this.removeClass(t):this.addClass(t)};var S=e(379),E=e.n(S),Z=e(795),H=e.n(Z),P=e(569),O=e.n(P),Y=e(565),B=e.n(Y),R=e(216),G=e.n(R),Q=e(589),U=e.n(Q),F=e(693),W={};W.styleTagTransform=U(),W.setAttributes=B(),W.insert=O().bind(null,"head"),W.domAPI=H(),W.insertStyleElement=G(),E()(F.Z,W),F.Z&&F.Z.locals&&F.Z.locals;let _={};Shiny.addCustomMessageHandler("WinBox-show",(t=>{var e=t.options;e.html=``,e.onclose=function(){Shiny.unbindAll(n),delete _[e.id]},e.onresize=function(t,o){i()("#shiny-winbox-"+e.id).find(".html-widget, .shiny-plot-output").trigger("resize")},_.hasOwnProperty(e.id)&&_[e.id].close();var o=new A(e),n=i()("#shiny-winbox-"+e.id);Shiny.renderContent(n,{html:t.html,deps:t.deps}),!e.hasOwnProperty("height")&&t.auto_height&&setTimeout((function(){o.height=n.height()+45,o.resize()}),100),_[o.id]=o})),Shiny.addCustomMessageHandler("WinBox-close",(t=>{if(t.hasOwnProperty("id"))_.hasOwnProperty(t.id)&&(_[t.id].close(),delete _[t.id]);else{var i=Object.keys(_),e=i.length-1;e>-1&&(_[i[e]].close(),delete _[i[e]])}}))})()})();
--------------------------------------------------------------------------------