Created
April 18, 2022 01:50
-
-
Save nanxstats/095c4b13eae339faf1783c96390d71cf to your computer and use it in GitHub Desktop.
Shiny file input area (Bootstrap 5)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fileInputArea <- function(inputId, label, multiple = FALSE, accept = NULL, | |
width = NULL, buttonLabel = "Browse...", placeholder = "No file selected") { | |
restoredValue <- restoreInput(id = inputId, default = NULL) | |
# Catch potential edge case - ensure that it's either NULL or a data frame. | |
if (!is.null(restoredValue) && !is.data.frame(restoredValue)) { | |
warning("Restored value for ", inputId, " has incorrect format.") | |
restoredValue <- NULL | |
} | |
if (!is.null(restoredValue)) { | |
restoredValue <- toJSON(restoredValue, strict_atomic = FALSE) | |
} | |
inputTag <- tags$input( | |
id = inputId, | |
name = inputId, | |
type = "file", | |
# Don't use "display: none;" style, which causes keyboard accessibility issue; instead use the following workaround: https://css-tricks.com/places-its-tempting-to-use-display-none-but-dont/ | |
style = "position: absolute !important; top: -99999px !important; left: -99999px !important;", | |
`data-restore` = restoredValue | |
) | |
if (multiple) { | |
inputTag$attribs$multiple <- "multiple" | |
} | |
if (length(accept) > 0) { | |
inputTag$attribs$accept <- paste(accept, collapse = ",") | |
} | |
div( | |
class = "form-group shiny-input-container w-100", | |
style = htmltools::css(width = htmltools::validateCssUnit(width)), | |
shiny:::shinyInputLabel(inputId, ""), | |
div( | |
class = "input-group mb-3", | |
# input-group-prepend is for bootstrap 4 compat | |
tags$label( | |
class = "input-group-btn input-group-prepend w-100", | |
span( | |
class = "btn btn-area w-100", inputTag, | |
div(tags$image(src = icon_encoded, width = "80px;"), style = "margin-top: 2rem;"), | |
div(p(label), style = "font-size: 1.2rem; font-weight: 700; padding-top: 2rem;"), | |
div(p(buttonLabel), style = "font-size: 1rem; font-weight: 400; margin-bottom: 2rem;") | |
) | |
) | |
), | |
tags$div( | |
id = paste(inputId, "_progress", sep = ""), | |
class = "progress active shiny-file-input-progress", | |
tags$div(class = "progress-bar") | |
) | |
) | |
} | |
# Use Bootstrap 5 colors $gray-700 and $gray-600 | |
css_btn_area <- textConnection(" | |
.btn-area { | |
color: #495057; | |
border-color: #495057; | |
border-style: dashed; | |
border-width: 2px; | |
border-radius: 20px; | |
background-color: transparent; | |
} | |
.btn-area:hover { | |
color: #6c757d; | |
} | |
.progress { | |
height: 32px; | |
} | |
.progress .progress-bar { | |
font-size: 16px; | |
line-height: 28px; | |
}") | |
# Icon from <https://icons.getbootstrap.com/icons/upload/> | |
icon_file <- tempfile(fileext = ".svg") | |
writeLines(' | |
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#495057" class="bi bi-upload" viewBox="0 0 16 16"> | |
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/> | |
<path d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z"/> | |
</svg>', | |
con = icon_file | |
) | |
icon_encoded <- xfun::base64_uri(icon_file) | |
card <- function(title, ...) { | |
htmltools::tags$div( | |
class = "card", | |
htmltools::tags$div(class = "card-header", title), | |
htmltools::tags$div(class = "card-body", ...) | |
) | |
} | |
ui <- shiny::fluidPage( | |
title = "File input area for Shiny (Bootstrap 5)", | |
theme = bslib::bs_theme(version = 5), | |
lang = "en", | |
includeCSS(css_btn_area), | |
shiny::fluidRow( | |
style = "margin-top: 20px;", | |
shiny::column( | |
width = 10, offset = 1, | |
card( | |
title = "File input area for Shiny (Bootstrap 5)", | |
shiny::fluidRow( | |
column( | |
width = 4, offset = 4, | |
fileInputArea( | |
"upload", | |
label = "Drop your plain text files here!", | |
buttonLabel = "Any plain text file, max 100 KB each.", | |
multiple = TRUE, | |
accept = "text/plain" | |
), | |
shiny::tableOutput("files") | |
) | |
) | |
) | |
) | |
) | |
) | |
server <- function(input, output, session) { | |
output$files <- renderTable( | |
input$upload[, c("name", "type", "size")], | |
width = "100%" | |
) | |
} | |
options(shiny.maxRequestSize = 0.1 * 1024^2) | |
shiny::shinyApp(ui, server) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment