Nothing
#' Save a dtsmartr widget to an HTML file
#'
#' Exports any `data.frame` as a fully interactive, standalone HTML file powered
#' by the dtsmartr widget. The resulting file can be opened in any modern web
#' browser without an active R session or internet connection, making it ideal
#' for offline use and sharing with collaborators.
#'
#' @param data A `data.frame` (or object coercible to one) to explore.
#' @param file Character string. Path to the output HTML file. The `.html`
#' extension is appended automatically if omitted.
#' @param selfcontained Logical. When `TRUE` (default), all JavaScript, CSS, and
#' data are embedded directly inside the HTML file, producing a single portable
#' file. When `FALSE`, a companion `<file>_files/` directory is created next
#' to the HTML file containing the JS/CSS assets - both must be kept together
#' when sharing. Use `FALSE` for large datasets where a single file would be
#' impractically large.
#' @param title Character string. Browser tab / window title for the saved HTML
#' page. Defaults to `"dtsmartr"`.
#' @param open Logical. When `TRUE` and the session is interactive, the saved
#' HTML file is opened automatically in the default web browser immediately
#' after saving. Defaults to `FALSE`.
#' @param background Character string. CSS colour for the page background.
#' Defaults to `"white"`.
#' @param libdir Character string or `NULL`. When `selfcontained = FALSE`, the
#' path to write the dependency libraries. Passed directly to
#' [htmlwidgets::saveWidget()]. Defaults to `NULL` (creates `<file>_files/`
#' next to the output file).
#' @param width Numeric or `NULL`. Widget width in pixels. `NULL` (default)
#' uses the full page width.
#' @param height Numeric or `NULL`. Widget height in pixels. `NULL` (default)
#' fills the viewport (maximised mode).
#' @param elementId Character string or `NULL`. CSS id for the widget's
#' root `<div>`. Defaults to `NULL` (auto-generated).
#' @param options Named list of UI options generated by [dtsmartr_options()].
#' @param verbose Logical. When `TRUE`, prints a confirmation message with the
#' resolved absolute path of the saved file. Defaults to `TRUE`.
#'
#' @return The absolute path to the saved HTML file, invisibly.
#'
#' @details
#' ## Selfcontained vs. non-selfcontained
#'
#' | `selfcontained` | Output | Best for |
#' |---|---|---|
#' | `TRUE` (default) | Single `.html` file | Email / sharing |
#' | `FALSE` | `.html` + `_files/` folder | Local use / large datasets |
#'
#' ## File size
#' Self-contained files embed the React runtime and widget code (~120 KB
#' gzipped) along with the data. For very large datasets (> 100 k rows) or
#' when generating many files, prefer `selfcontained = FALSE`.
#'
#' ## Browser compatibility
#' The generated HTML works in all modern browsers (Chrome, Firefox, Edge,
#' Safari 14+). No internet connection is required.
#'
#' @seealso [dtsmartr()] for creating the widget object interactively,
#' [htmlwidgets::saveWidget()] for the underlying save mechanism.
#'
#' @importFrom htmlwidgets saveWidget
#' @importFrom utils browseURL
#'
#' @examples
#' \donttest{
#' # == Basic usage =============================================================
#'
#' # Save mtcars as a self-contained HTML (single portable file)
#' tmp_file <- tempfile(fileext = ".html")
#' save_dtsmartr(mtcars, tmp_file)
#'
#' # Open in the browser right after saving
#' tmp_file_open <- tempfile(fileext = ".html")
#' save_dtsmartr(mtcars, tmp_file_open, open = TRUE)
#'
#' # == Custom options ==========================================================
#'
#' tmp_file_opts <- tempfile(fileext = ".html")
#' save_dtsmartr(
#' mtcars,
#' tmp_file_opts,
#' options = dtsmartr_options(hidden_columns = "cyl", advanced_filter = FALSE)
#' )
#' }
#'
#' @export
save_dtsmartr <- function(
data,
file,
selfcontained = TRUE,
title = "dtsmartr",
open = FALSE,
background = "white",
libdir = NULL,
width = NULL,
height = NULL,
elementId = NULL,
options = dtsmartr_options(),
verbose = TRUE
) {
# ── Input validation ────────────────────────────────────────────────────────
if (!is.data.frame(data)) {
stop("`data` must be a data.frame or an object coercible to one.",
call. = FALSE)
}
if (!is.character(file) || length(file) != 1L || nchar(trimws(file)) == 0L) {
stop("`file` must be a single non-empty character string.", call. = FALSE)
}
if (!is.logical(selfcontained) || length(selfcontained) != 1L) {
stop("`selfcontained` must be TRUE or FALSE.", call. = FALSE)
}
if (!is.logical(open) || length(open) != 1L) {
stop("`open` must be TRUE or FALSE.", call. = FALSE)
}
if (!is.logical(verbose) || length(verbose) != 1L) {
stop("`verbose` must be TRUE or FALSE.", call. = FALSE)
}
# ── Ensure .html extension ───────────────────────────────────────────────────
if (!grepl("\\.html?$", file, ignore.case = TRUE)) {
file <- paste0(file, ".html")
}
# ── Ensure output directory exists ──────────────────────────────────────────
out_dir <- dirname(file)
if (!dir.exists(out_dir)) {
dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)
}
# ── Pandoc check (required for selfcontained = TRUE) ────────────────────────
has_pandoc <- isTRUE(
tryCatch(
nzchar(rmarkdown::find_pandoc()$dir),
error = function(e) FALSE
)
)
if (isTRUE(selfcontained) && !has_pandoc) {
warning(
"selfcontained = TRUE requires pandoc, which was not found on this system.\n",
"Falling back to selfcontained = FALSE (HTML + companion '_files/' folder).\n",
"Install pandoc from https://pandoc.org/installing.html to enable single-file export.",
call. = FALSE
)
selfcontained <- FALSE
}
# ── Build widget ─────────────────────────────────────────────────────────────
# skip_routing = TRUE bypasses the >50k row re-route to dtsmartr_launch()
# so we always get a widget object back for saving — never a Shiny app.
widget <- dtsmartr(
data = data,
width = width,
height = height,
elementId = elementId,
options = options,
skip_routing = TRUE
)
# ── Save ─────────────────────────────────────────────────────────────────────
htmlwidgets::saveWidget(
widget = widget,
file = normalizePath(file, mustWork = FALSE),
selfcontained = selfcontained,
title = title,
background = background,
libdir = libdir
)
abs_path <- normalizePath(file, mustWork = TRUE)
# ── Reporting ────────────────────────────────────────────────────────────────
if (isTRUE(verbose)) {
mode_label <- if (selfcontained) "self-contained" else "with external assets"
message(
sprintf(
"dtsmartr saved (%s): %s",
mode_label,
abs_path
)
)
if (!selfcontained) {
message(
sprintf(
" -> Asset folder: %s",
file.path(dirname(abs_path),
paste0(tools::file_path_sans_ext(basename(abs_path)),
"_files"))
)
)
}
}
# ── Auto-open ────────────────────────────────────────────────────────────────
if (isTRUE(open) && interactive()) {
utils::browseURL(abs_path)
}
invisible(abs_path)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.