R/dtsmartr_launch.R

Defines functions dtsmartr_launch

Documented in dtsmartr_launch

#' Launch the dtsmartr Data Explorer in your default Web Browser
#'
#' Launches a temporary, lightweight Shiny server in the background and opens the
#' interactive grid in your default external web browser. This bypasses local
#' browser origin security policies (CORS) that block `file://` resources.
#'
#' @param data A `data.frame` to explore, or `NULL` (default) to launch the
#'   Zero-Code Data Ingestion Wizard.
#' @param port Optional port number. If `NULL` (default), a free port is chosen automatically.
#' @param options Named list of UI options generated by [dtsmartr_options()].
#'
#' @details
#' ## Zero-Code Ingestion Wizard (Wizard Mode)
#' When `data = NULL` (default), the Shiny server boots into Wizard Mode. An
#' interactive interface (powered by the `datamods` package) allows users to
#' upload local files (CSV, Excel, SAS, RDS). It also features direct "View"
#' and "Update" panels to verify and customize columns or variable classes
#' before loading.
#'
#' Once a file is successfully uploaded and confirmed, the wizard parses the dataset and feeds it
#' directly into the React-powered virtualized grid explorer.
#'
#' @importFrom shiny fluidPage tags HTML runApp conditionalPanel div h2 p reactive req outputOptions
#' @importFrom datamods import_ui import_server
#'
#' @return No return value, called for the side effect of starting a local
#'   background Shiny application and opening it in the default web browser.
#'
#' @export
#'
#' @examples
#' if (interactive()) {
#'   # 1. Launch wizard mode to upload local files
#'   dtsmartr_launch()
#'
#'   # 2. Launch directly with a dataset
#'   dtsmartr_launch(mtcars)
#' }
dtsmartr_launch <- function(
    data    = NULL,
    port    = NULL,
    options = dtsmartr_options()
) {

  if (!is.null(data)) {
    # ── Direct Mode: Render dataset immediately ──────────────────────────────
    if (!is.data.frame(data)) {
      stop("`data` must be a data.frame or NULL", call. = FALSE)
    }

    ds_name <- deparse(substitute(data))
    if (length(ds_name) > 1) ds_name <- paste(ds_name, collapse = "")
    if (nchar(ds_name) > 40 || grepl("[\\\\(\\\\)\\\\{\\\\}]", ds_name)) {
      ds_name <- "df"
    }

    ui <- shiny::fluidPage(
      title = paste("dtsmartr -", ds_name),
      shiny::tags$head(
        shiny::tags$style(shiny::HTML("
          body { margin: 0; padding: 0; overflow: hidden; background: #fff; }
          #grid { height: 100vh !important; }
        "))
      ),
      dtsmartrOutput("grid", width = "100%", height = "100vh")
    )

    server <- function(input, output, session) {
      output$grid <- renderDtsmartr({
        dtsmartr(data, datasetName = ds_name, options = options)
      })
    }

  } else {
    # ── Wizard Mode: File Ingestion with View and Update ───────────────────────
    theme_val <- if (requireNamespace("bslib", quietly = TRUE)) {
      bslib::bs_theme(version = 5, bootswatch = "flatly")
    } else {
      NULL
    }

    ui <- shiny::fluidPage(
      theme = theme_val,
      title = "dtsmartr Ingestion Wizard",
      shiny::tags$head(
        shiny::tags$style(shiny::HTML("
          body { background: #f8fafc; font-family: system-ui, -apple-system, sans-serif; }
          .wizard-card {
            max-width: 900px; margin: 60px auto; padding: 40px;
            background: #ffffff; border-radius: 16px;
            border: 1px solid #e2e8f0;
            box-shadow: 0 15px 35px -5px rgba(0, 0, 0, 0.05), 0 10px 10px -5px rgba(0, 0, 0, 0.02);
          }
          #grid { height: 100vh !important; }
        "))
      ),
      # UI visible only when no data is loaded
      shiny::conditionalPanel(
        condition = "!output.has_data",
        shiny::div(
          class = "wizard-card",
          shiny::h2("dtsmartr Data Ingestion Wizard",
                    style = "font-weight: 800; color: #0f172a; margin-bottom: 8px; text-align: center; letter-spacing: -0.025em;"),
          shiny::p("Upload CSV, Excel (.xlsx), SAS (.sas7bdat), or RDS datasets to start exploring.",
                   style = "color: #64748b; text-align: center; margin-bottom: 35px; font-size: 15px;"),
          datamods::import_ui(
            id              = "importer",
            from            = "file",
            file_extensions = c(".csv", ".xlsx", ".sas7bdat", ".rds")
          )
        )
      ),
      # Fullscreen UI visible when data is loaded
      shiny::conditionalPanel(
        condition = "output.has_data",
        shiny::div(
          style = "position: fixed; inset: 0; overflow: hidden; background: #fff;",
          dtsmartrOutput("grid", width = "100%", height = "100vh")
        )
      )
    )

    server <- function(input, output, session) {
      imported <- datamods::import_server(
        id = "importer"
      )

      has_data <- shiny::reactive({
        !is.null(imported$data()) && is.data.frame(imported$data())
      })

      output$has_data <- shiny::reactive({
        has_data()
      })
      shiny::outputOptions(output, "has_data", suspendWhenHidden = FALSE)

      output$grid <- renderDtsmartr({
        shiny::req(has_data())
        df <- imported$data()
        dtsmartr(df, datasetName = imported$name(), options = options)
      })
    }
  }

  # Launch default external browser on the background Shiny server
  shiny::runApp(
    appDir         = list(ui = ui, server = server),
    launch.browser = TRUE,
    port           = port,
    quiet          = TRUE
  )
}

Try the dtsmartr package in your browser

Any scripts or data that you put into this service are public.

dtsmartr documentation built on June 17, 2026, 1:08 a.m.