diff --git a/R/combineWidgets.R b/R/combineWidgets.R index 1356be2..296086b 100644 --- a/R/combineWidgets.R +++ b/R/combineWidgets.R @@ -114,19 +114,96 @@ combineWidgets <- function(..., list = NULL, nrow = NULL, ncol = NULL, title = N header = NULL, footer = NULL, leftCol = NULL, rightCol = NULL, width = NULL, height = NULL) { - widgets <- lapply(c(list(...), list), function(x) { - if (is.atomic(x)) return(structure(list(x = as.character(x)), class = "html")) - if (is.null(x$preRenderHook)) { - if (is(x, "htmlwidget")) return(x) - else return(structure(list(x = as.character(x)), class = "html")) - } - x$preRenderHook(x) + + widgets <- c(list(...), list) + if (length(widgets) == 0) return(combineWidgets("")) + + # create empty widget + res <- htmlwidgets::createWidget( + name = 'combineWidgets', + x = NULL, + width = width, + height = height, + package = 'manipulateWidget', + sizingPolicy = htmlwidgets::sizingPolicy( + browser.fill = TRUE + ), + preRenderHook = preRenderCombinedWidgets + ) + + # Add dependencies of embedded widgets + deps <- lapply(widgets, function(x) { + if (is.null(attr(x, "package"))) return(NULL) + append(getDependency(class(x)[1], attr(x, "package")), x$dependencies) }) - nwidgets <- length(widgets) + deps <- do.call(c, deps) + + res$dependencies <- deps + + # Add widget list and parameters + res$widgets <- widgets + res$params <- list( + nrow = nrow, + ncol = ncol, + title = title, + rowsize = rowsize, + colsize = colsize, + byrow = byrow, + titleCSS = titleCSS, + header = header, + footer = footer, + leftCol = leftCol, + rightCol = rightCol, + width = width, + height = height + ) - if (nwidgets == 0) return(combineWidgets("")) + res +} + +#' Shiny bindings for combineWidgets +#' +#' Output and render functions for using combineWidgets 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 combineWidgets +#' @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. +#' +#' @name combineWidgets-shiny +#' +#' @export +combineWidgetsOutput <- function(outputId, width = '100%', height = '400px'){ + htmlwidgets::shinyWidgetOutput(outputId, 'combineWidgets', width, height, package = 'manipulateWidget') +} + +#' @rdname combineWidgets-shiny +#' @export +renderCombineWidgets <- function(expr, env = parent.frame(), quoted = FALSE) { + if (!quoted) { expr <- substitute(expr) } # force quoted + htmlwidgets::shinyRenderWidget(expr, combineWidgetsOutput, env, quoted = TRUE) +} + +# Private function used to prerender a combinedWidgets object +preRenderCombinedWidgets <- function(x) { + widgets <- lapply(x$widgets, function(w) { + if (is.atomic(w)) return(structure(list(x = as.character(w)), class = "html")) + if (is.null(w$preRenderHook)) { + if (is(w, "htmlwidget")) return(w) + else return(structure(list(x = as.character(w)), class = "html")) + } + w$preRenderHook(w) + }) + nwidgets <- length(x$widgets) # Get number of rows and cols + nrow <- x$params$nrow + ncol <- x$params$ncol if (!is.null(nrow) && !is.null(ncol) && nrow * ncol < nwidgets) { stop("There are too much widgets compared to the number of rows and columns") } else if (is.null(nrow) && !is.null(ncol)) { @@ -141,13 +218,13 @@ combineWidgets <- function(..., list = NULL, nrow = NULL, ncol = NULL, title = N ncells <- nrow * ncol # Relative size of rows and cols - rowsize <- rep(rowsize, length.out=nrow) - colsize <- rep(colsize, length.out = ncol) + rowsize <- rep(x$params$rowsize, length.out=nrow) + colsize <- rep(x$params$colsize, length.out = ncol) # Get the html ID of each widget - elementId <- sapply(widgets[1:ncells], function(x) { - if (is.null(x)) res <- NULL - else res <- x$elementId + elementId <- sapply(widgets[1:ncells], function(w) { + if (is.null(w)) res <- NULL + else res <- w$elementId if (is.null(res)) res <- paste0("widget", floor(stats::runif(1, max = 1e9))) @@ -155,13 +232,13 @@ combineWidgets <- function(..., list = NULL, nrow = NULL, ncol = NULL, title = N }) # Construct the html of the combined widget - dirClass <- ifelse(byrow, "cw-by-row", "cw-by-col") + dirClass <- ifelse(x$params$byrow, "cw-by-row", "cw-by-col") widgetEL <- mapply( function(id, size) { sprintf('