diff --git a/DESCRIPTION b/DESCRIPTION index 55bbb56..472b20e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,8 +13,10 @@ RoxygenNote: 7.3.1 Imports: htmltools, htmlwidgets, + rlang, shiny Suggests: bslib, data.table, - ggplot2 + ggplot2, + patchwork diff --git a/NAMESPACE b/NAMESPACE index 3fc22b7..c1c539a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(create_design) export(gridstack) export(gridstackOutput) export(gridstack_proxy) @@ -22,6 +23,8 @@ importFrom(htmlwidgets,createWidget) importFrom(htmlwidgets,shinyRenderWidget) importFrom(htmlwidgets,shinyWidgetOutput) importFrom(htmlwidgets,sizingPolicy) +importFrom(rlang,"%||%") +importFrom(rlang,expr) importFrom(shiny,exprToFunction) importFrom(shiny,getDefaultReactiveDomain) importFrom(shiny,insertUI) diff --git a/R/design.R b/R/design.R new file mode 100644 index 0000000..ccd4afb --- /dev/null +++ b/R/design.R @@ -0,0 +1,27 @@ + +#' Create a patchwork design +#' +#' @param grid_layout Layout of the GridStack retrieved via `input$_layout`. +#' +#' @return An expression that can be evaluated to create a design usable in {patchwork}. +#' @export +#' +#' @importFrom rlang expr %||% +#' +#' @example examples/patchwork-design.R +create_design <- function(grid_layout) { + if (is.null(grid_layout$children)) + stop("grid_layout must be the value stored in `input$_layout`") + design <- lapply( + X = grid_layout$children, + FUN = function(x) { + t <- x$y + 1 + l <- x$x + 1 + b <- x$y + x$h %||% 1 + r <- x$x + x$w %||% 1 + expr(area(!!t, !!l, !!b, !!r)) + } + ) + expr(c(!!!design)) +} + diff --git a/R/gridstack.R b/R/gridstack.R index 0dbc2ee..baf2228 100644 --- a/R/gridstack.R +++ b/R/gridstack.R @@ -30,6 +30,7 @@ #' @param bg Background color. #' @inheritParams htmlwidgets::createWidget #' +#' #' @importFrom htmlwidgets createWidget sizingPolicy #' @importFrom htmltools renderTags #' @@ -102,35 +103,30 @@ gridstack <- function(..., ) } -#' #' @importFrom htmltools tags -#' gridstack_html <- function(id, style, class, ...) { -#' tags$div( -#' id = id, -#' class = class, -#' class = "grid-stack", -#' style = style, -#' ... -#' ) -#' } -#' Shiny bindings for gridstack + + + +#' @title Shiny bindings for gridstack #' +#' @description #' Output and render functions for using gridstack within Shiny #' applications and interactive Rmd documents. #' -#' @param outputId output variable to read from -#' @param width,height Must be a valid CSS unit (like \code{'100\%'}, -#' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a -#' string and have \code{'px'} appended. -#' @param expr An expression that generates a gridstack -#' @param env The environment in which to evaluate \code{expr}. -#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This -#' is useful if you want to save an expression in a variable. +#' @inheritParams htmlwidgets::shinyWidgetOutput +#' @inheritParams htmlwidgets::shinyRenderWidget +#' +#' @note +#' The GridStack layout can be retrieved via the special shiny input `input$_layout`. #' #' @name gridstack-shiny #' @importFrom htmlwidgets shinyWidgetOutput shinyRenderWidget #' @export -gridstackOutput <- function(outputId, width = "100%", height = "400px"){ +#' +#' @example examples/shiny.R +#' +#' @example examples/shiny-input.R +gridstackOutput <- function(outputId, width = "100%", height = "400px") { htmlwidgets::shinyWidgetOutput(outputId, "gridstack", width, height, package = "gridstackr") } diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..af19637 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,3 @@ +utils::globalVariables(c( + "area" # from patchwork +)) diff --git a/examples/patchwork-design.R b/examples/patchwork-design.R new file mode 100644 index 0000000..0130cb5 --- /dev/null +++ b/examples/patchwork-design.R @@ -0,0 +1,67 @@ + +library(shiny) +library(bslib) +library(ggplot2) +library(gridstackr) +library(patchwork) + +ui <- page_fluid( + tags$h2("Create {patchwork} design"), + fluidRow( + column( + width = 6, + tags$b("Create your design:"), + gridstackOutput("grid") + ), + column( + width = 6, + tags$b("Result:"), + plotOutput("design") + ) + ) +) + +server <- function(input, output, session) { + + output$grid <- renderGridstack({ + gridstack( + resize_handles = "se,e,s", + gs_item( + plotOutput("plot1", height = "100%") + ), + gs_item( + plotOutput("plot2", height = "100%") + ), + gs_item( + plotOutput("plot3", height = "100%") + ), + gs_item( + plotOutput("plot4", height = "100%") + ) + ) + }) + + output$plot1 <- renderPlot({ + ggplot(mtcars) + geom_point(aes(mpg, disp)) + }) + outputOptions(output, "plot1", suspendWhenHidden = TRUE) + output$plot2 <- renderPlot({ + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) + }) + output$plot3 <- renderPlot({ + ggplot(mtcars) + geom_smooth(aes(disp, qsec)) + }) + output$plot4 <- renderPlot({ + ggplot(mtcars) + geom_bar(aes(carb)) + }) + + output$design <- renderPlot({ + req(input$grid_layout) + mydesign <- create_design(input$grid_layout) + plot(eval(mydesign)) + }) + +} + +if (interactive()) + shinyApp(ui, server) diff --git a/man/create_design.Rd b/man/create_design.Rd new file mode 100644 index 0000000..97f321f --- /dev/null +++ b/man/create_design.Rd @@ -0,0 +1,86 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/design.R +\name{create_design} +\alias{create_design} +\title{Create a patchwork design} +\usage{ +create_design(grid_layout) +} +\arguments{ +\item{grid_layout}{Layout of the GridStack retrieved via \verb{input$_layout}.} +} +\value{ +An expression that can be evaluated to create a design usable in {patchwork}. +} +\description{ +Create a patchwork design +} +\examples{ + +library(shiny) +library(bslib) +library(ggplot2) +library(gridstackr) +library(patchwork) + +ui <- page_fluid( + tags$h2("Create {patchwork} design"), + fluidRow( + column( + width = 6, + tags$b("Create your design:"), + gridstackOutput("grid") + ), + column( + width = 6, + tags$b("Result:"), + plotOutput("design") + ) + ) +) + +server <- function(input, output, session) { + + output$grid <- renderGridstack({ + gridstack( + resize_handles = "se,e,s", + gs_item( + plotOutput("plot1", height = "100\%") + ), + gs_item( + plotOutput("plot2", height = "100\%") + ), + gs_item( + plotOutput("plot3", height = "100\%") + ), + gs_item( + plotOutput("plot4", height = "100\%") + ) + ) + }) + + output$plot1 <- renderPlot({ + ggplot(mtcars) + geom_point(aes(mpg, disp)) + }) + outputOptions(output, "plot1", suspendWhenHidden = TRUE) + output$plot2 <- renderPlot({ + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) + }) + output$plot3 <- renderPlot({ + ggplot(mtcars) + geom_smooth(aes(disp, qsec)) + }) + output$plot4 <- renderPlot({ + ggplot(mtcars) + geom_bar(aes(carb)) + }) + + output$design <- renderPlot({ + req(input$grid_layout) + mydesign <- create_design(input$grid_layout) + plot(eval(mydesign)) + }) + +} + +if (interactive()) + shinyApp(ui, server) +} diff --git a/man/gridstack-shiny.Rd b/man/gridstack-shiny.Rd index 7cbb4d5..c985cab 100644 --- a/man/gridstack-shiny.Rd +++ b/man/gridstack-shiny.Rd @@ -4,17 +4,7 @@ \alias{gridstack-shiny} \alias{gridstackOutput} \alias{renderGridstack} -\title{#' @importFrom htmltools tags -gridstack_html <- function(id, style, class, ...) { -tags$div( -id = id, -class = class, -class = "grid-stack", -style = style, -... -) -} -Shiny bindings for gridstack} +\title{Shiny bindings for gridstack} \usage{ gridstackOutput(outputId, width = "100\%", height = "400px") @@ -23,11 +13,12 @@ renderGridstack(expr, env = parent.frame(), quoted = FALSE) \arguments{ \item{outputId}{output variable to read from} -\item{width, height}{Must be a valid CSS unit (like \code{'100\%'}, -\code{'400px'}, \code{'auto'}) or a number, which will be coerced to a -string and have \code{'px'} appended.} +\item{width, height}{Must be a valid CSS unit (like \code{"100\%"}, +\code{"400px"}, \code{"auto"}) or a number, which will be coerced to a +string and have \code{"px"} appended.} -\item{expr}{An expression that generates a gridstack} +\item{expr}{An expression that generates an HTML widget (or a +\href{https://rstudio.github.io/promises/}{promise} of an HTML widget).} \item{env}{The environment in which to evaluate \code{expr}.} @@ -38,3 +29,106 @@ is useful if you want to save an expression in a variable.} Output and render functions for using gridstack within Shiny applications and interactive Rmd documents. } +\note{ +The GridStack layout can be retrieved via the special shiny input \verb{input$_layout}. +} +\examples{ + +library(shiny) +library(bslib) +library(ggplot2) +library(gridstackr) + +ui <- page_fluid( + tags$h2("GridStack example"), + gridstack( + margin = "10px", + cellHeight = "140px", + float = TRUE, + gs_item(value_box( + title = "Customer lifetime value", + value = "$5,000", + showcase = icon("bank"), + theme = "text-success", + class = "mb-0" + ), w = 6, h = 1), + gs_item(value_box( + title = "Customer lifetime value", + value = "$5,000", + showcase = icon("bank"), + theme = value_box_theme(bg = "#e6f2fd", fg = "#0B538E"), + class = "border mb-0" + ), w = 6, h = 1), + gs_item( + plotOutput("plot1", height = "100\%"), + w = 4, h = 2, class_content = "bg-white p-2 border rounded-4" + ), + gs_item( + plotOutput("plot2", height = "100\%"), + w = 4, h = 2, class_content = "bg-white p-2 border rounded-4" + ), + gs_item( + plotOutput("plot3", height = "100\%"), + w = 4, h = 2, class_content = "bg-white p-2 border rounded-4" + ), + gs_item( + plotOutput("plot4", height = "100\%"), + w = 12, h = 2, class_content = "bg-white p-2 border rounded-4" + ) + ) +) + +server <- function(input, output, session) { + + output$plot1 <- renderPlot({ + ggplot(mtcars) + geom_point(aes(mpg, disp)) + }) + output$plot2 <- renderPlot({ + ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear)) + }) + output$plot3 <- renderPlot({ + ggplot(mtcars) + geom_smooth(aes(disp, qsec)) + }) + output$plot4 <- renderPlot({ + ggplot(mtcars) + geom_bar(aes(carb)) + }) + +} + +if (interactive()) + shinyApp(ui, server) + +library(shiny) +library(bslib) +library(gridstackr) + +ui <- page_fluid( + tags$h2("GridStack Input example"), + gridstackOutput("mygrid"), + verbatimTextOutput("res") +) + +server <- function(input, output, session) { + + output$mygrid <- renderGridstack({ + gridstack( + minRow = 2, + margin = "0.2rem", + gs_item("1", id = "item_1", w = 4, h = 2, class_content = "gs-item-example"), + gs_item("2", id = "item_2", w = 5, class_content = "gs-item-example"), + gs_item("3", id = "item_3", w = 5, x = 4, y = 2, class_content = "gs-item-example"), + gs_item("4", id = "item_4", h = 2, w = 3, class_content = "gs-item-example"), + gs_item("5", id = "item_5", w = 6, x = 0, y = 3, class_content = "gs-item-example"), + gs_item("6", id = "item_6", w = 6, x = 6, y = 3, class_content = "gs-item-example") + ) + }) + + output$res <- renderPrint({ + data.table::rbindlist(input$mygrid_layout$children, fill = TRUE) + }) + +} + +if (interactive()) + shinyApp(ui, server) +}