bslib icon indicating copy to clipboard operation
bslib copied to clipboard

Offcanvas Element

Open DavZim opened this issue 9 months ago • 0 comments

In the documentation on popovers vs modals, you mention that offcanvas elements are on the roadmap, but I couldn't find it anywhere here on GitHub.

For an internal project I implemented a quick-and-dirty solution like this and I didn't want it to go to "waste".

Let me know if something like this is what you had in mind for an offcanvas bslib implementation.

offcanvas <- function(el,
                      header,
                      body,
                      header_btn_close = TRUE,
                      width = "600px",
                      position = c("start", "end", "top", "bottom"),
                      ns = identity) {
  position <- match.arg(position)
  id <- ns("offcanvas")

  el <- el |>
    shiny::tagAppendAttributes(
      "data-bs-toggle" = "offcanvas",
      "data-bs-target" = paste0("#", id),
      "aria-controls" = "offcanvasExample"
    )

  shiny::tagList(
    el,
    shiny::div(
      class = sprintf("offcanvas offcanvas-%s", position),
      style = sprintf("width: %s;", width),
      tabindex = "-1",
      id = id,
      shiny::div(
        class = "offcanvas-header",
        shiny::div(
          class = "offcanvas-title",
          header
        ),
        if (header_btn_close) {
          shiny::actionButton(ns("btn_close"), NULL, class = "btn-close") |>
            shiny::tagAppendAttributes(
              "data-bs-dismiss" = id,
              "data-bs-toggle" = "offcanvas",
              "aria-label" = "Close"
            )
        }
      ),
      shiny::div(
        class = "offcanvas-body",
        body
      )
    )
  )
}

Which you can then use like this:

library(shiny)
# source("R/offcanvas.R") # to get the function above...

offcanvas_element <- offcanvas(
  width = "30vw",
  position = "end",
  el = actionButton("go", bsicons::bs_icon("gear")),
  header = "This is an offcanvas",
  body = tagList(
    textInput("mytext", "Text Input"),
    
    hr(),
    
    actionButton("cncl", "Cancel") |> 
      shiny::tagAppendAttributes(
        "data-bs-dismiss" = "offcanvas",
        "data-bs-toggle" = "offcanvas",
        "aria-label" = "Close"
      )
  )
)

ui <- bslib::page_fillable(
  bslib::card(
    bslib::card_header("My Card", offcanvas_element),
    "Some content that may be affected by the offcanvas."
  )
)

server <- function(input, output, session) {}

shinyApp(ui, server)

Which results in an app like this

Image

DavZim avatar Mar 26 '25 07:03 DavZim