Search code examples
rshinybslib

shiny::downloadButton in bslib::card_header with icon instead of text


I wish to include a downloadButton on the right side of my bslib::card. Ideally, this would be an icon. What I have done so far is

require(shiny)
require(bslib)
require(ggplot2)

ui <- bslib::page_fillable(
    theme=bslib::bs_theme(version=5),
    title = "Card with Download Icon",
    bslib::card(
        bslib::card_header(
            class = "d-flex justify-content-between",
            "Card Title",
            shiny::downloadButton(
                outputId="download",
                label="",
                icon=shiny::icon("image") # way too large
            )
        ),
        shiny::plotOutput("the_plot")
    )
)

server <- function(input, output, session) {
    plot_obj <- shiny::reactive(
        ggplot2::qplot(Sepal.Length, Sepal.Width, data = iris)
    )
    output$the_plot <- shiny::renderPlot(plot_obj())
    output$download <- shiny::downloadHandler(
        filename = function() "card_snapshot.png",
        content = function(file) {
            ggplot2::ggsave(
                filename=file, plot=plot_obj(), device=png,
                width=16, height=9, units="cm", bg="white"
            )
        }
    )
}

shiny::shinyApp(ui, server)

but this results in a too large card header, i.e.

screenshot

I wonder how I can create a smaller icon for download?

I also tried shiny::downloadLink, but there seems to be not icon argument...

Any hint appreciated!


Solution

  • You're creating a button with outputId = "download", so you can add some css to your ui to select this element by ID (#download) and change the padding.

    tags$head(
            tags$style(
                HTML("
                    #download {
                        padding: 0px 5px;
                    }")
            )
        )
    

    Output

    enter image description here

    In this instance it's not actually necessary to wrap your css in htmltools::HTML(). However, it will become necessary if your style becomes more complicated and starts to use characters that you need to specify should not be escaped, like examples here which import fonts. I think it's good practice to use HTML() always when writing html or css in your R code, and then you don't have to think about whether you're using characters that might be escaped.

    Complete ui code

    ui <- bslib::page_fillable(
        tags$head(
            tags$style(
                HTML("
                    #download {
                        padding: 0px 5px;
                    }")
            )
        ),
        theme = bslib::bs_theme(version = 5),
        title = "Card with Download Icon",
        bslib::card(
            bslib::card_header(
                class = "d-flex justify-content-between",
                "Card Title",
                shiny::downloadButton(
                    outputId = "download",
                    label = "",
                    icon = shiny::icon("image") # way too large
                )
            ),
            shiny::plotOutput("the_plot")
        )
    )