| Title: | Tools to Make Modular and Interactive R Workflow Builders |
|---|---|
| Description: | Provides the building blocks for modular, interactive shiny applications that turn point-and-click pipelines into reproducible R and R Markdown code. Using shinymeta, shinypal assembles code modules into a draggable workflow, shows the equivalent code as the user builds it, and lets them export the whole pipeline as a runnable script or rendered report. |
| Authors: | William Gearty [aut, cre] (ORCID: <https://orcid.org/0000-0003-0076-3262>) |
| Maintainer: | William Gearty <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.0.0.9000 |
| Built: | 2026-07-07 19:57:08 UTC |
| Source: | https://github.com/willgearty/shinypal |
A bslib::accordion_panel() pre-wired with a "Remove this step" button and
the data-rank-id attribute shinypal's sortable workflow needs. Use it as
the panel returned by a step's fun_workflow.
accordion_panel_remove_button(ind, ...)accordion_panel_remove_button(ind, ...)
ind |
The index of the step. |
... |
Additional arguments passed to |
A bslib::accordion_panel() tag with value = "step_<ind>" and a
matching data-rank-id attribute.
Other step UI:
df_modal_button(),
select_column_input(),
select_dataset_input(),
varname_input(),
verbatimTextOutput_copy()
accordion_panel_remove_button(1, "My step")accordion_panel_remove_button(1, "My step")
Convenience wrapper around the boilerplate shared by every step that
produces an intermediate dataset: it stores the data reactive, wires
the data-preview modal, registers the step via add_shinypal_step(),
and (optionally) keeps the dataset dropdown and column selectors in
sync. A module only supplies the data reactive and its UI/report
functions.
add_shinypal_data_step( ind, data, fun_workflow, fun_report, libs = character(0), code_guard = NULL, ec_subs = NULL, select_dataset = FALSE, column_ids = character(0), rename = TRUE )add_shinypal_data_step( ind, data, fun_workflow, fun_report, libs = character(0), code_guard = NULL, ec_subs = NULL, select_dataset = FALSE, column_ids = character(0), rename = TRUE )
ind |
The index of the step. |
data |
A |
fun_workflow |
A function that generates the UI elements for the workflow. |
fun_report |
A function that generates the UI elements for the report. |
libs |
A character vector of R packages required for this step. |
code_guard |
An optional zero-argument function evaluated inside the
code output before |
ec_subs |
An optional list of length 2, where the first element is a metaReactive object and the second element is a callback function. This is used to substitute expansion contexts in the code chain. |
select_dataset |
Whether the step consumes an upstream dataset (and thus
needs a |
column_ids |
A character vector of column-selector input ids to keep in
sync via |
rename |
Whether to render a |
Called for its side effects and returns NULL invisibly. Stores the
step's data reactive under its step_varname() id (e.g. data_1), wires
its code output and data-preview modal, registers the step via
add_shinypal_step(), and (when requested) installs the dataset/column
selector observers and, when rename = TRUE, the dataset-rename field and
its observer.
Other workflow steps:
add_shinypal_plot_step(),
add_shinypal_step(),
next_step_index(),
step_varname()
## Not run: # inside a module's server.R ind <- next_step_index() occs <- shinymeta::metaReactive2(varname = step_varname(ind), { shinymeta::metaExpr(head(mtcars, input[[paste0("n_", ind)]])) }) add_shinypal_data_step( ind, data = occs, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Subset"), fun_report = function(ind) verbatimTextOutput_copy(ind) ) ## End(Not run)## Not run: # inside a module's server.R ind <- next_step_index() occs <- shinymeta::metaReactive2(varname = step_varname(ind), { shinymeta::metaExpr(head(mtcars, input[[paste0("n_", ind)]])) }) add_shinypal_data_step( ind, data = occs, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Subset"), fun_report = function(ind) verbatimTextOutput_copy(ind) ) ## End(Not run)
Convenience wrapper around the boilerplate shared by every step that
renders a plot: it assigns the rendered plot to its output slot, renders
the step's generated code, registers the step via add_shinypal_step(),
and (optionally) keeps the dataset dropdown and column selectors in sync. A
module only supplies the plot render and its UI/report functions.
Unlike add_shinypal_data_step(), a plot step does not store an
intermediate dataset or show a data-preview modal; its result is a figure,
not a selectable data.frame.
add_shinypal_plot_step( ind, plot, fun_workflow, fun_report, libs = character(0), code_guard = NULL, output_prefix = "plot_", select_dataset = TRUE, column_ids = character(0) )add_shinypal_plot_step( ind, plot, fun_workflow, fun_report, libs = character(0), code_guard = NULL, output_prefix = "plot_", select_dataset = TRUE, column_ids = character(0) )
ind |
The index of the step. |
plot |
A |
fun_workflow |
A function that generates the UI elements for the workflow. |
fun_report |
A function that generates the UI elements for the report. |
libs |
A character vector of R packages required for this step. |
code_guard |
An optional zero-argument function evaluated inside the
code output before |
output_prefix |
The prefix for the plot's output slot, combined with
|
select_dataset |
Whether the step consumes an upstream dataset (and thus
needs a |
column_ids |
A character vector of column-selector input ids to keep in
sync via |
Called for its side effects and returns NULL invisibly. Assigns
the rendered plot to output[[<output_prefix><ind>]], renders the step's
generated code, registers the step via add_shinypal_step(), and (when
requested) installs the dataset/column selector observers.
Other workflow steps:
add_shinypal_data_step(),
add_shinypal_step(),
next_step_index(),
step_varname()
## Not run: # inside a module's server.R ind <- next_step_index() p <- shinymeta::metaRender2(shiny::renderPlot, { df <- get_int_data(input[[paste0("dataset_", ind)]])() shinymeta::metaExpr(plot(df)) }) add_shinypal_plot_step( ind, plot = p, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Plot"), fun_report = function(ind) shiny::plotOutput(paste0("plot_", ind)) ) ## End(Not run)## Not run: # inside a module's server.R ind <- next_step_index() p <- shinymeta::metaRender2(shiny::renderPlot, { df <- get_int_data(input[[paste0("dataset_", ind)]])() shinymeta::metaExpr(plot(df)) }) add_shinypal_plot_step( ind, plot = p, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Plot"), fun_report = function(ind) shiny::plotOutput(paste0("plot_", ind)) ) ## End(Not run)
Registers a single step with shinypal's reactive state. It inserts the
step's panel into the workflow accordion, appends the step's block to
the report, adds the step's quoted code to the code chain used to
assemble the reproducible script, and records any packages the step
needs. When ec_subs is supplied, it also registers an expansion-context
substitution so the downloadable script can swap in alternate code (for
example, fully-qualified calls).
Most data-producing steps should instead use add_shinypal_data_step(),
which wraps this together with the data storage, code output, and selector
boilerplate. Call add_shinypal_step() directly for steps that don't fit
that pattern.
add_shinypal_step( ind, fun_workflow, fun_report, code_chain_list, libs = character(0), ec_subs = NULL )add_shinypal_step( ind, fun_workflow, fun_report, code_chain_list, libs = character(0), ec_subs = NULL )
ind |
The index of the step. |
fun_workflow |
A function that generates the UI elements for the workflow. |
fun_report |
A function that generates the UI elements for the report. |
code_chain_list |
A list of quoted code bits that will be added to code_chain(). |
libs |
A character vector of R packages required for this step. |
ec_subs |
An optional list of length 2, where the first element is a metaReactive object and the second element is a callback function. This is used to substitute expansion contexts in the code chain. |
Called for its side effects and returns NULL invisibly. The step
is registered with shinypal's reactive state (its panel is inserted into
the workflow accordion, its block appended to the report, its quoted code
added to the code chain, and its packages recorded), and a remove-button
observer is installed for it.
Other workflow steps:
add_shinypal_data_step(),
add_shinypal_plot_step(),
next_step_index(),
step_varname()
## Not run: # inside a module's server.R, which shinypal_setup() sources with local = TRUE ind <- next_step_index() add_shinypal_step( ind, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Head"), fun_report = function(ind) verbatimTextOutput_copy(ind), code_chain_list = list(quote(head(mtcars))), libs = "utils" ) ## End(Not run)## Not run: # inside a module's server.R, which shinypal_setup() sources with local = TRUE ind <- next_step_index() add_shinypal_step( ind, fun_workflow = function(ind) accordion_panel_remove_button(ind, "Head"), fun_report = function(ind) verbatimTextOutput_copy(ind), code_chain_list = list(quote(head(mtcars))), libs = "utils" ) ## End(Not run)
Generates an event observer that watches for when a copy button is clicked.
Upon clicking, the code_expr is evaluated and copied to the clipboard
using clipr::write_clip().
clip_observe(ind, code_expr)clip_observe(ind, code_expr)
ind |
The index of the step. |
code_expr |
An expression (generated by |
Called for its side effects; invisibly returns the observer.
verbatimTextOutput_copy(), whose copy button it wires.
Other step observers:
column_select_observe(),
df_modal_observe(),
df_select_observe(),
file_observe(),
var_name_observe()
## Not run: clip_observe(ind, rlang::expr(get_chunk(ind))) ## End(Not run)## Not run: clip_observe(ind, rlang::expr(get_chunk(ind))) ## End(Not run)
varSelectInput of data.frame column names up-to-dateInstalls an observer that keeps a shiny::varSelectInput() of column names
in sync with the dataset currently chosen in the step's dataset_<ind>
dropdown. Pair with select_dataset_input() and select_column_input().
column_select_observe(ind, inputId)column_select_observe(ind, inputId)
ind |
The index of the step. |
inputId |
The id of the |
Called for its side effects; invisibly returns the observer.
select_column_input(), the selector this observer updates.
Other step observers:
clip_observe(),
df_modal_observe(),
df_select_observe(),
file_observe(),
var_name_observe()
## Not run: column_select_observe(ind, paste0("column_", ind)) ## End(Not run)## Not run: column_select_observe(ind, paste0("column_", ind)) ## End(Not run)
Generates a shiny::actionButton() that, when clicked, generates a modal
for the resulting dataset for the specified step. Make sure to set up a
corresponding observer using df_modal_observe().
df_modal_button(ind, text = "View data")df_modal_button(ind, text = "View data")
ind |
The index of the step. |
text |
The text to display on the button. |
A shiny::actionButton() with id df_modal_<ind>.
df_modal_observe(), which opens the modal this button triggers.
Other step UI:
accordion_panel_remove_button(),
select_column_input(),
select_dataset_input(),
varname_input(),
verbatimTextOutput_copy()
df_modal_button(1)df_modal_button(1)
Installs an observer that opens a modal showing a DT::datatable() of the
named intermediate dataset when the step's "view data" button is clicked.
Pair with df_modal_button().
df_modal_observe(ind, df_name)df_modal_observe(ind, df_name)
ind |
The index of the step. |
df_name |
The name of the data.frame to be displayed. |
Called for its side effects; invisibly returns the observer.
df_modal_button(), the button that opens this modal.
Other step observers:
clip_observe(),
column_select_observe(),
df_select_observe(),
file_observe(),
var_name_observe()
## Not run: df_modal_observe(ind, step_varname(ind)) ## End(Not run)## Not run: df_modal_observe(ind, step_varname(ind)) ## End(Not run)
selectInput of intermediate data.frames up-to-dateInstalls an observer that keeps the step's dataset dropdown
(dataset_<ind>) populated with the available upstream datasets,
preserving the current selection where possible and otherwise defaulting to
the most recent one.
df_select_observe(ind)df_select_observe(ind)
ind |
The index of the step. |
Called for its side effects; invisibly returns the observer.
select_dataset_input(), the dropdown this observer updates.
Other step observers:
clip_observe(),
column_select_observe(),
df_modal_observe(),
file_observe(),
var_name_observe()
## Not run: df_select_observe(ind) ## End(Not run)## Not run: df_select_observe(ind) ## End(Not run)
Installs an observer that records the file uploaded through inputId so it
is bundled into the downloadable report archive, keyed by the upload's
original file name.
file_observe(inputId)file_observe(inputId)
inputId |
The id of the |
Called for its side effects; invisibly returns the observer.
Other step observers:
clip_observe(),
column_select_observe(),
df_modal_observe(),
df_select_observe(),
var_name_observe()
## Not run: file_observe("user_file") ## End(Not run)## Not run: file_observe("user_file") ## End(Not run)
Returns the code chunk for ind, expanded with a shared context across
all currently-registered steps. Use this inside a reactive consumer
(e.g., renderPrint(), observeEvent()) to display or copy a step's
code. The chunk is rebuilt whenever any dependency changes.
get_chunk(ind)get_chunk(ind)
ind |
The index of the step. |
A code object suitable for printing or passing to
shinymeta::displayCodeModal(), or NULL if the step is not registered.
Other generated code:
workflow_has_errors()
## Not run: # inside renderPrint() or observeEvent() in a module get_chunk(ind) ## End(Not run)## Not run: # inside renderPrint() or observeEvent() in a module get_chunk(ind) ## End(Not run)
Maps a step index to a reproducible accent color (with a readable foreground color) drawn from the khroma "smooth rainbow" palette, so each workflow step gets a distinct, stable color for its accordion header and report border.
get_colors(ind)get_colors(ind)
ind |
The index of the step. |
A list with two elements: color, the foreground color
("black" or "white", chosen for contrast against the background), and
background, the accent color as a hex string.
Other utilities:
is_shinylive()
get_colors(1) get_colors(2)$backgroundget_colors(1) get_colors(2)$background
Retrieves a stored intermediate data reactive by name (the counterpart to
set_int_data()). Call the returned reactive to obtain the data.
get_int_data(name)get_int_data(name)
name |
The name of the data object to retrieve. |
The stored reactive; call it (e.g. get_int_data(name)()) to get
the data. Propagates a req() failure if name is not registered.
Other intermediate data:
get_int_dfs(),
set_int_data()
## Not run: df <- get_int_data(input[[paste0("dataset_", ind)]])() ## End(Not run)## Not run: df <- get_int_data(input[[paste0("dataset_", ind)]])() ## End(Not run)
Returns the names of intermediate datasets produced by steps earlier than
ind in the current workflow order, so a step can only ever consume
upstream output. Used to populate the dataset selectors.
get_int_dfs(ind)get_int_dfs(ind)
ind |
The index of the step. |
A named character vector of intermediate datasets in workflow
order: values are the stable internal ids (step_varname(), e.g. data_1)
and names are the display labels (a custom name if set, otherwise the id).
Empty if none are available upstream.
Other intermediate data:
get_int_data(),
set_int_data()
## Not run: # inside a reactive or observer in a module, after shinypal_setup() get_int_dfs(ind) ## End(Not run)## Not run: # inside a reactive or observer in a module, after shinypal_setup() get_int_dfs(ind) ## End(Not run)
Returns TRUE when the app is running in a shinylive/webR session (i.e.
compiled to WebAssembly via Emscripten) and FALSE in a normal R session.
Useful for gating behavior that can't run in the browser, such as loading
packages with no WebAssembly build or bundling a downloadable zip.
is_shinylive()is_shinylive()
A length-1 logical: TRUE under shinylive/webR, otherwise FALSE.
Other utilities:
get_colors()
is_shinylive()is_shinylive()
Returns a unique, monotonically increasing index for a new workflow step.
Each call increments a server-side counter. The counter is initialized by
shinypal_setup() and is deliberately never reset while the session is
running.
next_step_index()next_step_index()
A single positive integer to use as the new step's index.
Other workflow steps:
add_shinypal_data_step(),
add_shinypal_plot_step(),
add_shinypal_step(),
step_varname()
## Not run: ind <- next_step_index() ## End(Not run)## Not run: ind <- next_step_index() ## End(Not run)
This should be paired with select_dataset_input().
select_column_input(ind, label = "Choose a column:", default = NULL, ...)select_column_input(ind, label = "Choose a column:", default = NULL, ...)
ind |
The index of the step. |
label |
The label for the select input. |
default |
The default value for the select input. |
... |
Additional arguments passed to |
A shiny::varSelectInput() tag with id column_<ind>.
column_select_observe(), which keeps this selector in sync.
Other step UI:
accordion_panel_remove_button(),
df_modal_button(),
select_dataset_input(),
varname_input(),
verbatimTextOutput_copy()
## Not run: select_column_input(ind) ## End(Not run)## Not run: select_column_input(ind) ## End(Not run)
A shiny::selectInput() listing the intermediate datasets available to
step ind (those produced by earlier steps), defaulting to the most
recent. Keep its choices current with df_select_observe().
select_dataset_input(ind, label = "Choose a dataset:")select_dataset_input(ind, label = "Choose a dataset:")
ind |
The index of the step. |
label |
The label for the select input. |
A shiny::selectInput() tag with id dataset_<ind>.
df_select_observe(), which keeps this dropdown populated.
Other step UI:
accordion_panel_remove_button(),
df_modal_button(),
select_column_input(),
varname_input(),
verbatimTextOutput_copy()
## Not run: select_dataset_input(ind) ## End(Not run)## Not run: select_dataset_input(ind) ## End(Not run)
Stores a reactive data object in shinypal's intermediate-data registry
under name, making it available to later steps and to the assembled
script. Usually called for you by add_shinypal_data_step().
set_int_data(obj, name)set_int_data(obj, name)
obj |
A reactive data object to store. |
name |
A name to store data object as. |
Called for its side effects; invisibly returns NULL.
Other intermediate data:
get_int_data(),
get_int_dfs()
## Not run: set_int_data(occs, step_varname(ind)) ## End(Not run)## Not run: set_int_data(occs, step_varname(ind)) ## End(Not run)
A function to set up the shinypal environment. This function should be called at the top of the server function of your shiny app.
shinypal_setup( input, output, session, modules, download_filename = "shinypal_script.zip", download_template = "./modules/test_report.qmd", prefix = "data_" )shinypal_setup( input, output, session, modules, download_filename = "shinypal_script.zip", download_template = "./modules/test_report.qmd", prefix = "data_" )
input |
The shiny input object. |
output |
The shiny output object. |
session |
The shiny session object. |
modules |
A character vector of paths to independent modules. |
download_filename |
The name of the file to download when the user clicks the download button. |
download_template |
The path to the Quarto markdown template file that will be used to generate the report. |
prefix |
Prefix for each data step's internal id and the variable name it
gets in the generated script (default |
Each module should have a ui-aux.R file that defines the UI elements for
that module in the "Workflow" accordion and a server.R file that defines
the server-side logic for the module.
Called for its side effects and returns NULL invisibly. It
initializes shinypal's shared reactive state, renders the live report and
wires the download handler and workflow observers, then sources each
module's ui-aux.R and server.R so their steps become available.
Other app setup:
shinypal_ui()
## Not run: server <- function(input, output, session) { modules <- list.dirs("./modules", recursive = FALSE) shinypal_setup(input, output, session, modules) } ## End(Not run)## Not run: server <- function(input, output, session) { modules <- list.dirs("./modules", recursive = FALSE) shinypal_setup(input, output, session, modules) } ## End(Not run)
A function to create the UI for the ShinyPal app. This function should be called in the UI function of your shiny app.
shinypal_ui(modules)shinypal_ui(modules)
modules |
A character vector of paths to independent modules. |
Each module should have a ui-main.R file that defines the UI elements for
that module in the "Steps" accordion.
A shiny::tagList() holding the shinypal interface: a sidebar
layout with the "Possible Workflow Steps" and "Workflow" cards on the left
and the live report/download sidebar on the right. Drop it into your app's
UI, for example inside a bslib::page_navbar() panel.
Other app setup:
shinypal_setup()
## Not run: ui <- function() { modules <- list.dirs("./modules", recursive = FALSE) bslib::page_navbar(title = "My app", bslib::nav_panel("Build", shinypal_ui(modules))) } ## End(Not run)## Not run: ui <- function() { modules <- list.dirs("./modules", recursive = FALSE) bslib::page_navbar(title = "My app", bslib::nav_panel("Build", shinypal_ui(modules))) } ## End(Not run)
Returns the name shinypal uses both as a data step's stored-dataset id and
as the variable it is assigned in the generated script (set prefix in
shinypal_setup()). A module should pass this as the varname of its
shinymeta::metaReactive2() so the emitted variable name matches the id
shinypal stores and renames.
step_varname(ind)step_varname(ind)
ind |
The index of the step. |
A length-1 character string, the prefix followed by ind
(e.g., "data_1").
Other workflow steps:
add_shinypal_data_step(),
add_shinypal_plot_step(),
add_shinypal_step(),
next_step_index()
## Not run: # inside a module's add-step observer, after shinypal_setup() step_varname(ind) ## End(Not run)## Not run: # inside a module's add-step observer, after shinypal_setup() step_varname(ind) ## End(Not run)
Installs an observer on the step's varname_<ind> text input. A valid,
unique R variable name is recorded in shinypal's var_names registry,
becoming the dataset's label in later selectors and its symbol in the
generated script. Pair with varname_input().
var_name_observe(ind)var_name_observe(ind)
ind |
The index of the step. |
Called for its side effects; invisibly returns the observer.
varname_input(), the input this observer watches.
Other step observers:
clip_observe(),
column_select_observe(),
df_modal_observe(),
df_select_observe(),
file_observe()
## Not run: var_name_observe(ind) ## End(Not run)## Not run: var_name_observe(ind) ## End(Not run)
A shiny::textInput() (id varname_<ind>) for naming the dataset a step
produces. The name becomes the dataset's label in later selectors and its
variable name in the generated script. Pair with var_name_observe(), which validates
the entry; add_shinypal_data_step() inserts both automatically when
rename = TRUE.
varname_input(ind, label = "Name this dataset (optional):")varname_input(ind, label = "Name this dataset (optional):")
ind |
The index of the step. |
label |
The label for the text input. |
A shiny::textInput() tag with id varname_<ind>.
var_name_observe(), which validates and stores the entry.
Other step UI:
accordion_panel_remove_button(),
df_modal_button(),
select_column_input(),
select_dataset_input(),
verbatimTextOutput_copy()
## Not run: varname_input(ind) ## End(Not run)## Not run: varname_input(ind) ## End(Not run)
Render a reactive output variable as text within an application
page. Uses shiny::verbatimTextOutput() which is usually paired with
shiny::renderPrint() and provides fixed-width text in a <pre>. Make
sure to set up a corresponding observer using clip_observe().
verbatimTextOutput_copy(ind)verbatimTextOutput_copy(ind)
ind |
The index of the step. |
A htmltools::div() wrapping a shiny::verbatimTextOutput() (id
code_<ind>) and a copy shiny::actionButton() (id copy_<ind>).
clip_observe(), which copies the displayed code to the clipboard.
Other step UI:
accordion_panel_remove_button(),
df_modal_button(),
select_column_input(),
select_dataset_input(),
varname_input()
verbatimTextOutput_copy(1)verbatimTextOutput_copy(1)
Returns TRUE if any step in the current workflow cannot be expanded into
the downloadable script due to failing req()/validate() checks.
workflow_has_errors()workflow_has_errors()
A length-one logical.
Other generated code:
get_chunk()
## Not run: if (workflow_has_errors()) { shiny::showNotification("Some steps are incomplete.") } ## End(Not run)## Not run: if (workflow_has_errors()) { shiny::showNotification("Some steps are incomplete.") } ## End(Not run)