Skip to content

Commit

Permalink
Merge pull request #82 from ashbaldry/feature/download-file-name
Browse files Browse the repository at this point in the history
Allow custom UI file names
  • Loading branch information
ashbaldry authored Jun 18, 2023
2 parents 2dd86b9 + ca2199a commit 4576879
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 87 deletions.
34 changes: 28 additions & 6 deletions R/json_to_rscript.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@
#'
#' @param json A string containing JSON code of the "App UI" page
#' @param module_name Optional string the allows the function to be written as a module
#' @param app_type Structure of the application. Either `app`, with an app.R/ui.R and server.R,
#' `golem` or `rhino` with the relevant project structure.
#'
#' @return A string that can be written to a \code{ui.R} file
#'
#' @noRd
jsonToRScript <- function(json, module_name = NULL) {
jsonToRScript <- function(json, module_name = NULL, app_type = c("app", "golem", "rhino")) {
if (is.null(json)) return("")

valid_json <- jsonlite::validate(json)

if (valid_json) {
app_type <- match.arg(app_type)
html_list <- jsonlite::fromJSON(json, simplifyDataFrame = FALSE)
htmlToRScript(html_list, module_name = module_name)
htmlToRScript(html_list, module_name = module_name, app_type = app_type)
} else {
message(attr(valid_json, "err"), "Returning NA")
NA_character_
Expand All @@ -37,10 +40,11 @@ jsonToRScript <- function(json, module_name = NULL) {
#' @return A string that can be written to a \code{ui.R} file
#'
#' @noRd
htmlToRScript <- function(html_list, indent = 0L, module_name = NULL) {
htmlToRScript <- function(html_list, indent = 0L, module_name = NULL, app_type = c("app", "golem", "rhino")) {
if (is.null(html_list$r_function)) return("")
app_type <- match.arg(app_type)

module_home <- indent == 0L && is.character(module_name)
module_home <- indent == 0L && (is.character(module_name) || app_type != "app")
indent_space <- paste0(rep(" ", indent), collapse = "")
indent_text_space <- paste0(rep(" ", indent + 2L), collapse = "")

Expand Down Expand Up @@ -80,9 +84,27 @@ htmlToRScript <- function(html_list, indent = 0L, module_name = NULL) {
r_comments <- paste0(indent_space, "# ", strsplit(html_list$r_comments, "\n")[[1L]], "\n", collapse = "")
}

if (module_home) {
if (module_home && is.character(module_name)) {
r_comments <- paste0("#' ", module_name, " Module\n#' @export\n")
rfunc <- paste0(gsub("\\W", "", tools::toTitleCase(module_name)), "UI <- function(id) {\n tagList(\n")
if (app_type == "rhino") {
pkgs <- c("shiny", if (html_list$r_function == "dashboardPage") "shinydashboard")
r_comments <- paste0("box::use(", toString(pkgs), ")\n\n", r_comments)

Check warning on line 91 in R/json_to_rscript.R

View check run for this annotation

Codecov / codecov/patch

R/json_to_rscript.R#L90-L91

Added lines #L90 - L91 were not covered by tests
}

rfunc <- paste0(
gsub("\\W", "", tools::toTitleCase(module_name)),
"UI <- function(id) {\n tagList(\n"
)
rfunc_arguments <- NULL
rfunc_end <- " )\n}"
} else if (module_home) {
r_comments <- paste0("#' Application UI \n#' @export\n")
if (app_type == "rhino") {
pkgs <- c("shiny", if (html_list$r_function == "dashboardPage") "shinydashboard")
r_comments <- paste0("box::use(", toString(pkgs), ")\n\n", r_comments)

Check warning on line 104 in R/json_to_rscript.R

View check run for this annotation

Codecov / codecov/patch

R/json_to_rscript.R#L101-L104

Added lines #L101 - L104 were not covered by tests
}

rfunc <- paste0("AppUI <- function(id) {\n ", html_list$r_function, "(\n")
rfunc_arguments <- NULL
rfunc_end <- " )\n}"

Check warning on line 109 in R/json_to_rscript.R

View check run for this annotation

Codecov / codecov/patch

R/json_to_rscript.R#L107-L109

Added lines #L107 - L109 were not covered by tests
} else {
Expand Down
93 changes: 32 additions & 61 deletions R/mod_code_srv.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,59 @@
#' @noRd
CodeModuleServer <- function(id, ui_code) {
moduleServer(id, function(input, output, session) {
setBookmarkExclude(c("save", "save_confirm", "file_type", "file_name"))
setBookmarkExclude(c("save", "download", "file_type", "file_name", "options"))

Check warning on line 6 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L6

Added line #L6 was not covered by tests
ns <- session$ns

observeEvent(input$save, ignoreInit = TRUE, {
showModal(
modalDialog(
tagList(
shiny::radioButtons(
inputId = ns("file_type"),
label = "File Type:",
choices = c("UI" = "ui", "Module" = "module"),
inline = TRUE
),
conditionalPanel(
condition = "input.file_type === 'module'",
ns = ns,
tagList(
shiny::textInput(
inputId = ns("file_name"),
label = "Module Name:"
),
shiny::radioButtons(
inputId = ns("app_type"),
label = "App Structure:",
choices = c("{golem}" = "golem", "{rhino}" = "rhino"),
inline = TRUE
)
)
)
),
title = "Save UI",
footer = tagList(
tags$button(
type = "button",
class = "btn btn-secondary",
`data-dismiss` = "modal",
`data-bs-dismiss` = "modal",
shiny::icon("xmark"),
"Cancel"
),
tags$button(
id = ns("save_confirm"),
type = "button",
class = "btn btn-primary action-button",
`data-dismiss` = "modal",
`data-bs-dismiss` = "modal",
shiny::icon("check"),
"Confirm"
)
)
)
observeEvent(input$file_type, {
updateTextInput(
session = session,
inputId = "file_name",
label = switch(input$file_type, "ui" = "File Name", "module" = "Module Name"),
value = switch(input$file_type, "ui" = "ui.R", "module" = "Template"),

Check warning on line 14 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L9-L14

Added lines #L9 - L14 were not covered by tests
)
})

observeEvent(input$save_confirm, ignoreInit = TRUE, {
writeToUI(ui_code(), input$file_type, input$file_name,input$app_type)
observeEvent(input$save, ignoreInit = TRUE, {
writeToUI(ui_code(), input$file_type, input$file_name, input$app_type)

Check warning on line 19 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L18-L19

Added lines #L18 - L19 were not covered by tests
})

r_code <- reactive(jsonToRScript(ui_code()))

output$code <- renderPrint(cat(r_code()))

output$download <- downloadHandler(
filename = "ui.R",
filename = function() {
if (input$file_type == "ui") {
input$file_name

Check warning on line 25 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L23-L25

Added lines #L23 - L25 were not covered by tests
} else {
paste0("mod_", tolower(gsub("\\W", "_", input$file_name)), "_ui.R")

Check warning on line 27 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L27

Added line #L27 was not covered by tests
}
},
content = function(file) {
writeLines(r_code(), file)
module_name <- if (input$file_type == "ui") NULL else input$file_name
r_code <- jsonToRScript(ui_code(), module_name = module_name)
writeLines(r_code, file)

Check warning on line 33 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L31-L33

Added lines #L31 - L33 were not covered by tests
}
)

r_code <- reactive({
module_name <- if (input$file_type == "ui") NULL else input$file_name
jsonToRScript(ui_code(), module_name = module_name, app_type = input$app_type)

Check warning on line 39 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L37-L39

Added lines #L37 - L39 were not covered by tests
})

output$code <- renderPrint(cat(r_code()))

Check warning on line 42 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L42

Added line #L42 was not covered by tests
})
}

writeToUI <- function(code, file_type = c("ui", "module"), module_name = NULL, app_type = c("golem", "rhino")) {
writeToUI <- function(code, file_type = c("ui", "module"), module_name = NULL,
app_type = c("app", "golem", "rhino")) {
file_type <- match.arg(file_type)
app_type <- match.arg(app_type)

Check warning on line 49 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L48-L49

Added lines #L48 - L49 were not covered by tests

r_dir <- switch(app_type, "app" = ".", "golem" = "R", "rhino" = "app/view")
if (!file.exists(r_dir)) dir.create(r_dir, recursive = TRUE)

Check warning on line 52 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L51-L52

Added lines #L51 - L52 were not covered by tests

if (file_type == "ui") {
r_code <- jsonToRScript(code)
file_name <- "ui.R"
file_name <- file.path(r_dir, module_name)

Check warning on line 56 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L54-L56

Added lines #L54 - L56 were not covered by tests
} else {
r_code <- jsonToRScript(code, module_name = module_name)
r_dir <- if (app_type == "golem") "R" else "app/view"
if (!file.exists(r_dir)) dir.create(r_dir, recursive = TRUE)
file_name <- file.path(r_dir, paste0("mod_", tolower(gsub(" ", "_", module_name)), "_ui.R"))

Check warning on line 59 in R/mod_code_srv.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_srv.R#L58-L59

Added lines #L58 - L59 were not covered by tests
}

Expand Down
74 changes: 58 additions & 16 deletions R/mod_code_ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,68 @@ CodeModUI <- function(id) {
tagList(
tags$form(
class = "code-ui-form",
span(
toast("copy_toast", "Copied!"),
tags$button(
class = "copy-ui-button btn btn-default",
role = "button",
icon("copy"),
"Copy"
tags$fieldset(
span(
toast("copy_toast", "Copied!"),
tags$button(
class = "copy-ui-button btn btn-default",
role = "button",
icon("copy"),
"Copy"
)
),
downloadButton(
ns("download")
),
if (interactive()) {
actionButton(
ns("save"),
"Save As...",
icon("floppy-disk")

Check warning on line 35 in R/mod_code_ui.R

View check run for this annotation

Codecov / codecov/patch

R/mod_code_ui.R#L34-L35

Added lines #L34 - L35 were not covered by tests
)
},
actionButton(
ns("options"),
icon("cogs"),
title = "Saving options"
)
),
downloadButton(
ns("download")
),
if (interactive()) {
actionButton(
ns("save"),
"Save As...",
shiny::icon("floppy-disk")

tags$fieldset(
id = ns("options_fields"),
class = "save-code-options",
style = "display: none;",

tagAppendAttributes(
radioButtons(
inputId = ns("file_type"),
label = "File Type",
choices = c("UI" = "ui", "Module" = "module"),
inline = TRUE
),
class = "form-inline"
),
tagAppendAttributes(
textInput(
inputId = ns("file_name"),
label = "File Name",
width = "100%",
value = "ui.R"
),
class = "form-inline"
),
tagAppendAttributes(
radioButtons(
inputId = ns("app_type"),
label = "App Structure",
choices = c("Stanard" = "app", "{golem}" = "golem", "{rhino}" = "rhino"),
inline = TRUE
),
class = "form-inline"
)
}
)
),

tagAppendAttributes(
verbatimTextOutput(ns("code"), placeholder = TRUE),
class = "code-output"
Expand Down
27 changes: 26 additions & 1 deletion inst/app/www/designer.css
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,31 @@ h5.modal-title {
height: 50px;
}

#settings-code_dropdown {
max-height: 75vh;
}

.show#settings-code_dropdown {
display: flex;
flex-direction: column;
}

.save-code-options {
margin-top: 0.5rem;
}

.save-code-options .form-inline>label {
margin-right: 0.5rem;
}

.save-code-options .shiny-options-group {
display: flex;
}

.save-code-options input[type="radio"] {
margin-right: 0.25rem;
}

.code-ui-form {
margin: 0 0.5rem 0.5rem 0.5rem;
}
Expand All @@ -307,7 +332,7 @@ h5.modal-title {

.code-output {
overflow-y: auto;
height: 75vh;
min-height: 20vh;
margin-bottom: -0.5rem;
}

Expand Down
Loading

0 comments on commit 4576879

Please sign in to comment.