rstudio / bslib

Tools for theming Shiny and R Markdown via Bootstrap 3, 4, or 5.
https://rstudio.github.io/bslib/
Other
463 stars 56 forks source link

Conditionally show content in full screen cards #1102

Open gadenbuie opened 3 weeks ago

gadenbuie commented 3 weeks ago

Feature request

It'd be nice to directly support conditionally shown content in full-screen cards. Here's how you can get there today.

  1. Add conditional content in a card_body() with class = "card-reveal-full-screen".

    card(
      "A fancy table",
      card_body(
        class = "card-reveal-full-screen",
        reactableOutput("table")
      ),
      full_screen = TRUE
    )

    The class can be named anything else (in case you think of a better name); what's important is that it's coupled with some custom CSS in the next step.

  2. Use custom CSS to toggle the visibility of that card body area.

    .bslib-card[data-full-screen="false"] > .card-reveal-full-screen {
      display: none;
    }

    The above CSS hides elements with .card-reveal-full-screen, conveniently hiding the table output in the example above. By using [data-full-screen="false"] we also stay out of the way of the default presentation when in full-screen mode; in this case the card_body() around the table wants to have display: flex.

Example app

Here's a full example also available on shinylive.io/r:

library(shiny)
library(bslib)
library(reactable)

ui <- page_fluid(
  card(
    "A fancy table",
    card_body(
      class = "card-reveal-full-screen",
      reactableOutput("table")
    ),
    full_screen = TRUE
  ),
  includeCSS("card-reveal-full-screen.css")
)

server <- function(input, output) {
  output$table <- renderReactable({
    reactable(mtcars)
  })
}

shinyApp(ui = ui, server = server)
/* card-reveal-full-screen.css */
.bslib-card[data-full-screen="false"] > .card-reveal-full-screen {
  display: none;
}

API brainstorm

Two options that occur to me:

  1. We could add an argument to card_body(), e.g. card_body(show_when = c("both", "expanded", "collapsed")).
  2. Or we could add a new function wrapping card_body(), e.g. card_body_full_screen().

I lean toward adding an argument to card_body() for discoverability, backwards compat, etc.

Naming is also complicated. I can see a use case for both showing and hiding content in the full screen mode, e.g. having a short summary that's replaced with longer content (note this can also be done with dynamic UI, but I think it'd be easier to reason about with an explicit argument/function). This means that a "full screen" option would have a paired "not full screen" option, which I find less compelling than "expanded" and "collapsed".

gadenbuie commented 3 weeks ago

Also worth noting: there's a way to do this using the input${card_id}_full_screen reactive. I think there is room for both approaches, especially because the server-managed hiding is a little cumbersome. (See rstudio/htmltools#434.)

gadenbuie commented 3 weeks ago

The same approach works for value boxes but requires more complicated CSS:

.bslib-value-box[data-full-screen="false"] > .card-body > .value-box-area > .card-reveal-full-screen,
.bslib-value-box[data-full-screen="false"] > .card-body > .value-box-grid > .value-box-area > .card-reveal-full-screen {
  display: none;
}

Example app:

library(shiny)
library(bslib)
library(reactable)

ui <- page_fluid(
  value_box(
    title = "mtcars",
    value = sprintf("%d cars", nrow(mtcars)),
    card_body(
      class = "card-reveal-full-screen",
      reactableOutput("table")
    ),
    showcase = bsicons::bs_icon("car-front-fill"),
    showcase_layout = showcase_left_center(width_full_screen = "100px"),
    full_screen = TRUE
  ),
  includeCSS("card-reveal-full-screen.css")
)

server <- function(input, output) {
  output$table <- renderReactable({
    reactable(mtcars)
  })
}

shinyApp(ui = ui, server = server)