|
| 1 | +#' UI Component Factory Functions for cOmicsArt |
| 2 | +#' |
| 3 | +#' This file contains reusable UI component builders with consistent styling. |
| 4 | +#' These functions eliminate duplication and ensure visual consistency. |
| 5 | +#' |
| 6 | +#' @author cOmicsArt Development Team |
| 7 | +#' @date 2026-06-14 |
| 8 | + |
| 9 | +# Button Components ------------------------------------------------------------ |
| 10 | + |
| 11 | +#' Create a primary action button with cOmicsArt styling |
| 12 | +#' |
| 13 | +#' Primary buttons have the green background and are used for main actions. |
| 14 | +#' |
| 15 | +#' @param inputId Character. Button ID |
| 16 | +#' @param label Character. Button label |
| 17 | +#' @param icon_name Character. Font Awesome icon name (optional) |
| 18 | +#' @param full_width Logical. Make button 100% width (default: FALSE) |
| 19 | +#' @param width Character. Custom width (default: NULL, uses full_width if TRUE) |
| 20 | +#' @param ... Additional arguments passed to actionButton() |
| 21 | +#' |
| 22 | +#' @return Shiny action button with primary styling |
| 23 | +#' |
| 24 | +#' @examples |
| 25 | +#' \dontrun{ |
| 26 | +#' primary_action_button( |
| 27 | +#' inputId = "start_analysis", |
| 28 | +#' label = "Start Analysis", |
| 29 | +#' icon_name = "rocket", |
| 30 | +#' full_width = TRUE |
| 31 | +#' ) |
| 32 | +#' } |
| 33 | +#' |
| 34 | +#' @export |
| 35 | +primary_action_button <- function(inputId, label, icon_name = NULL, |
| 36 | + full_width = FALSE, width = NULL, ...) { |
| 37 | + icon_obj <- if (!is.null(icon_name)) icon(icon_name) else NULL |
| 38 | + |
| 39 | + # Determine width |
| 40 | + if (is.null(width)) { |
| 41 | + width <- if (full_width) "100%" else NULL |
| 42 | + } |
| 43 | + |
| 44 | + actionButton( |
| 45 | + inputId = inputId, |
| 46 | + label = label, |
| 47 | + icon = icon_obj, |
| 48 | + width = width, |
| 49 | + style = STYLE_PRIMARY_BUTTON, |
| 50 | + ... |
| 51 | + ) |
| 52 | +} |
| 53 | + |
| 54 | +#' Create a secondary action button with cOmicsArt styling |
| 55 | +#' |
| 56 | +#' Secondary buttons have white background and are used for less prominent actions. |
| 57 | +#' |
| 58 | +#' @inheritParams primary_action_button |
| 59 | +#' @export |
| 60 | +secondary_action_button <- function(inputId, label, icon_name = NULL, |
| 61 | + full_width = FALSE, width = NULL, ...) { |
| 62 | + icon_obj <- if (!is.null(icon_name)) icon(icon_name) else NULL |
| 63 | + |
| 64 | + # Determine width |
| 65 | + if (is.null(width)) { |
| 66 | + width <- if (full_width) "100%" else NULL |
| 67 | + } |
| 68 | + |
| 69 | + actionButton( |
| 70 | + inputId = inputId, |
| 71 | + label = label, |
| 72 | + icon = icon_obj, |
| 73 | + width = width, |
| 74 | + style = STYLE_SECONDARY_BUTTON, |
| 75 | + ... |
| 76 | + ) |
| 77 | +} |
| 78 | + |
| 79 | +#' Create a transparent action button with cOmicsArt styling |
| 80 | +#' |
| 81 | +#' Transparent buttons have no background and are used for toggles or minimal actions. |
| 82 | +#' |
| 83 | +#' @inheritParams primary_action_button |
| 84 | +#' @export |
| 85 | +transparent_action_button <- function(inputId, label, icon_name = NULL, |
| 86 | + full_width = FALSE, width = NULL, ...) { |
| 87 | + icon_obj <- if (!is.null(icon_name)) icon(icon_name) else NULL |
| 88 | + |
| 89 | + # Determine width |
| 90 | + if (is.null(width)) { |
| 91 | + width <- if (full_width) "100%" else NULL |
| 92 | + } |
| 93 | + |
| 94 | + actionButton( |
| 95 | + inputId = inputId, |
| 96 | + label = label, |
| 97 | + icon = icon_obj, |
| 98 | + width = width, |
| 99 | + style = STYLE_TRANSPARENT_BUTTON, |
| 100 | + ... |
| 101 | + ) |
| 102 | +} |
| 103 | + |
| 104 | +# Download Components ---------------------------------------------------------- |
| 105 | + |
| 106 | +#' Create a download button with optional helper |
| 107 | +#' |
| 108 | +#' Creates a download button with consistent styling and optional help tooltip. |
| 109 | +#' |
| 110 | +#' @param outputId Character. Download handler ID |
| 111 | +#' @param label Character. Button label |
| 112 | +#' @param help_content Character. Markdown help content ID (optional) |
| 113 | +#' @param class Character. CSS class (default: "btn-default") |
| 114 | +#' @param ... Additional arguments passed to downloadButton() |
| 115 | +#' |
| 116 | +#' @return Download button with optional helper |
| 117 | +#' |
| 118 | +#' @examples |
| 119 | +#' \dontrun{ |
| 120 | +#' download_button_with_help( |
| 121 | +#' outputId = "download_results", |
| 122 | +#' label = "Download Results", |
| 123 | +#' help_content = "download_help" |
| 124 | +#' ) |
| 125 | +#' } |
| 126 | +#' |
| 127 | +#' @export |
| 128 | +download_button_with_help <- function(outputId, label, help_content = NULL, |
| 129 | + class = "btn-default", ...) { |
| 130 | + btn <- downloadButton( |
| 131 | + outputId = outputId, |
| 132 | + label = label, |
| 133 | + class = class, |
| 134 | + ... |
| 135 | + ) |
| 136 | + |
| 137 | + if (!is.null(help_content)) { |
| 138 | + # Attach helper if shinyhelper is available |
| 139 | + if (requireNamespace("shinyhelper", quietly = TRUE)) { |
| 140 | + btn <- btn %>% shinyhelper::helper(type = "markdown", content = help_content) |
| 141 | + } else { |
| 142 | + warning("shinyhelper package not available. Helper not attached to download button.") |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + btn |
| 147 | +} |
| 148 | + |
| 149 | +# Input Components ------------------------------------------------------------- |
| 150 | + |
| 151 | +#' Create a select input with standard width |
| 152 | +#' |
| 153 | +#' Creates a selectInput with the standard 80% width used throughout cOmicsArt. |
| 154 | +#' |
| 155 | +#' @param inputId Character. Input ID |
| 156 | +#' @param label Character. Label |
| 157 | +#' @param choices Vector. Choices |
| 158 | +#' @param selected Character. Selected value (default: "") |
| 159 | +#' @param width Character. Width (default: "80%") |
| 160 | +#' @param multiple Logical. Allow multiple selections (default: FALSE) |
| 161 | +#' @param ... Additional arguments passed to selectInput() |
| 162 | +#' |
| 163 | +#' @return Select input with standard width |
| 164 | +#' |
| 165 | +#' @examples |
| 166 | +#' \dontrun{ |
| 167 | +#' standard_select_input( |
| 168 | +#' inputId = "omic_type", |
| 169 | +#' label = "Omic Type", |
| 170 | +#' choices = c("Transcriptomics", "Lipidomics", "Metabolomics") |
| 171 | +#' ) |
| 172 | +#' } |
| 173 | +#' |
| 174 | +#' @export |
| 175 | +standard_select_input <- function(inputId, label, choices, selected = "", |
| 176 | + width = "80%", multiple = FALSE, ...) { |
| 177 | + selectInput( |
| 178 | + inputId = inputId, |
| 179 | + label = label, |
| 180 | + choices = choices, |
| 181 | + selected = selected, |
| 182 | + width = width, |
| 183 | + multiple = multiple, |
| 184 | + ... |
| 185 | + ) |
| 186 | +} |
| 187 | + |
| 188 | +#' Create a file input with standard width |
| 189 | +#' |
| 190 | +#' Creates a fileInput with the standard 80% width. |
| 191 | +#' |
| 192 | +#' @param inputId Character. Input ID |
| 193 | +#' @param label Character or HTML. Label (can include HTML for links) |
| 194 | +#' @param accept Character vector. Accepted file types (default: c(".csv")) |
| 195 | +#' @param width Character. Width (default: "80%") |
| 196 | +#' @param ... Additional arguments passed to fileInput() |
| 197 | +#' |
| 198 | +#' @return File input with standard width |
| 199 | +#' |
| 200 | +#' @examples |
| 201 | +#' \dontrun{ |
| 202 | +#' standard_file_input( |
| 203 | +#' inputId = "data_matrix", |
| 204 | +#' label = "Upload Data Matrix", |
| 205 | +#' accept = c(".csv", ".xlsx") |
| 206 | +#' ) |
| 207 | +#' } |
| 208 | +#' |
| 209 | +#' @export |
| 210 | +standard_file_input <- function(inputId, label, accept = c(".csv"), |
| 211 | + width = "80%", ...) { |
| 212 | + shiny::fileInput( |
| 213 | + inputId = inputId, |
| 214 | + label = label, |
| 215 | + accept = accept, |
| 216 | + width = width, |
| 217 | + ... |
| 218 | + ) |
| 219 | +} |
| 220 | + |
| 221 | +# Layout Components ------------------------------------------------------------ |
| 222 | + |
| 223 | +#' Create a triple split layout with identical buttons |
| 224 | +#' |
| 225 | +#' Creates a splitLayout with 3 identical buttons (action or download). |
| 226 | +#' Commonly used in volcano plot controls. |
| 227 | +#' |
| 228 | +#' @param ids Character vector of length 3. Button IDs |
| 229 | +#' @param label Character. Button label (same for all 3) |
| 230 | +#' @param class Character. Button class (default: "btn-default") |
| 231 | +#' @param type Character. "action" or "download" (default: "action") |
| 232 | +#' @param ... Additional arguments passed to button function |
| 233 | +#' |
| 234 | +#' @return Split layout with 3 buttons |
| 235 | +#' |
| 236 | +#' @examples |
| 237 | +#' \dontrun{ |
| 238 | +#' triple_button_layout( |
| 239 | +#' ids = c("copy_adj", "copy_raw", "copy_both"), |
| 240 | +#' label = "Copy to Clipboard", |
| 241 | +#' type = "action" |
| 242 | +#' ) |
| 243 | +#' } |
| 244 | +#' |
| 245 | +#' @export |
| 246 | +triple_button_layout <- function(ids, label, class = "btn-default", |
| 247 | + type = "action", ...) { |
| 248 | + if (length(ids) != 3) { |
| 249 | + stop("ids must be a vector of length 3") |
| 250 | + } |
| 251 | + |
| 252 | + if (type == "download") { |
| 253 | + # Download buttons use outputId |
| 254 | + button_fn <- function(id) { |
| 255 | + downloadButton(outputId = id, label = label, class = class, ...) |
| 256 | + } |
| 257 | + } else { |
| 258 | + # Action buttons use inputId |
| 259 | + button_fn <- function(id) { |
| 260 | + actionButton(inputId = id, label = label, class = class, ...) |
| 261 | + } |
| 262 | + } |
| 263 | + |
| 264 | + splitLayout( |
| 265 | + button_fn(ids[1]), |
| 266 | + button_fn(ids[2]), |
| 267 | + button_fn(ids[3]) |
| 268 | + ) |
| 269 | +} |
| 270 | + |
| 271 | +# Clipboard Button Components -------------------------------------------------- |
| 272 | + |
| 273 | +#' Create an action button with clipboard data attribute |
| 274 | +#' |
| 275 | +#' Creates an action button that automatically works with the generic |
| 276 | +#' JavaScript clipboard handler (no need to add JS code). |
| 277 | +#' |
| 278 | +#' @param inputId Character. Button ID |
| 279 | +#' @param label Character. Button label |
| 280 | +#' @param plot_id Character. ID of the plot to copy (for data-clipboard-plot attribute) |
| 281 | +#' @param icon_name Character. Font Awesome icon (default: "clipboard") |
| 282 | +#' @param class Character. CSS class (default: "btn-default") |
| 283 | +#' @param ... Additional arguments passed to actionButton() |
| 284 | +#' |
| 285 | +#' @return Action button with data-clipboard-plot attribute |
| 286 | +#' |
| 287 | +#' @examples |
| 288 | +#' \dontrun{ |
| 289 | +#' clipboard_button( |
| 290 | +#' inputId = "copy_pca_btn", |
| 291 | +#' label = "Copy PCA Plot", |
| 292 | +#' plot_id = "PCA_plot" |
| 293 | +#' ) |
| 294 | +#' } |
| 295 | +#' |
| 296 | +#' @export |
| 297 | +clipboard_button <- function(inputId, label = "Copy to Clipboard", plot_id, |
| 298 | + icon_name = "clipboard", class = "btn-default", ...) { |
| 299 | + actionButton( |
| 300 | + inputId = inputId, |
| 301 | + label = label, |
| 302 | + icon = icon(icon_name), |
| 303 | + class = class, |
| 304 | + `data-clipboard-plot` = plot_id, # This attribute triggers JS handler |
| 305 | + ... |
| 306 | + ) |
| 307 | +} |
| 308 | + |
| 309 | +# Helper Attachment ------------------------------------------------------------ |
| 310 | + |
| 311 | +#' Attach helper to any UI element |
| 312 | +#' |
| 313 | +#' Convenience wrapper for shinyhelper::helper that checks if package is available. |
| 314 | +#' |
| 315 | +#' @param ui_element Shiny UI element |
| 316 | +#' @param content Character. Markdown file name (without .md extension) |
| 317 | +#' @param type Character. Helper type (default: "markdown") |
| 318 | +#' @param ... Additional arguments passed to shinyhelper::helper() |
| 319 | +#' |
| 320 | +#' @return UI element with helper attached, or original element if shinyhelper unavailable |
| 321 | +#' |
| 322 | +#' @examples |
| 323 | +#' \dontrun{ |
| 324 | +#' selectInput("my_input", "Label", choices = c("A", "B")) %>% |
| 325 | +#' attach_helper("my_input_help") |
| 326 | +#' } |
| 327 | +#' |
| 328 | +#' @export |
| 329 | +attach_helper <- function(ui_element, content, type = "markdown", ...) { |
| 330 | + if (requireNamespace("shinyhelper", quietly = TRUE)) { |
| 331 | + ui_element %>% shinyhelper::helper(type = type, content = content, ...) |
| 332 | + } else { |
| 333 | + warning("shinyhelper package not available. Helper not attached.") |
| 334 | + ui_element |
| 335 | + } |
| 336 | +} |
0 commit comments