Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sidebar): Independently customize sidebar state on mobile and desktop screens #943

Merged
merged 23 commits into from
Dec 20, 2023

Conversation

gadenbuie
Copy link
Member

@gadenbuie gadenbuie commented Dec 14, 2023

Fixes #941

Custom screen-size initial state options

This PR allows users to choose the initial sidebar state for desktop and mobile devices. For example, the current open = "desktop" default is equivalent to

sidebar(
  open = list(desktop = "open", mobile = "closed")
)

You could achieve an always-open sidebar on mobile devices, with a collapsible sidebar on desktop devices with

sidebar(
  open = list(desktop = "open", mobile = "always")
)

If, as in the above case, the sidebar is always open in one size but collapsible in another, we need watch the window size and manually set the sidebar state if we move from a collapsible size to an always open size. Imagine a user collapsed the sidebar on a desktop-sized screen and then resizes the window to a mobile size (where we need to open the sidebar, in addition to ensuring that its visible).

Because of this, we also now better handle moving between small and large sizes when the sidebar is not collapsible at one of the two sizes. In the above case, when moving from mobile to desktop, we'll ensure the sidebar remains open, but if

sidebar(
  open = list(desktop = "closed", mobile = "always")
)

and the user moves from mobile to desktop, we'll follow the initial state and have the sidebar be closed when they cross into desktop sizes.

Side note: If the sidebar is always collapsible, we don't add the window resize event listener to avoid unnecessary event handlers. We also don't watch or update the collapsible/initial state attributes, although we do re-consult them. With a bit more time we could refactor the sidebar layout into a custom element or web component, in which case it'd make a lot of sense to make those attributes reactive.

Public API

In an early iteration of this PR, we added sidebar_open_on() to the public API. We are choosing not to export this function at this time, but we've kept it in the codebase and could easily export it in the future.

Page-level sidebars

In addition to the above changes, we also updated sidebar(open = NULL) as the new default value. This allows parent containers to take a sidebar argument and supply their own open default if not otherwise provided by the user. We use this in page_sidebar() and page_navbar() to make page-level sidebars use open = list(desktop = "open", mobile = "always") by default.

This motivated some internal refactoring to have sidebar() return the sidebar data itself but delay rendering to tags. I added a new S3 method for as.tags() for the class bslib_sidebar that is called by layout_sidebar() to render the collapse and sidebar tags using the data structure returned by sidebar() (and that may have even been modified between calling sidebar() and handing the data off to layout_sidebar()).

Just as I'd like to refactor the Typescript to use web components, I'd like to refactor sidebar() and layout_sidebar() to use R6 -- in the interest of delivering this new feature quickly I've done neither of those things 😇

Example App

Kapture.2023-12-14.at.17.08.02.mp4
App Code
ui <- page_navbar(
  title = "My Dashboard",
  fillable = FALSE,
  fillable_mobile = !TRUE,
  position = "static-top",
  sidebar = sidebar(
      sliderInput("n_bins", "Bins in First Plot", 0, 20, 7),
      sliderInput("n_bins2", "Bins in Second Plot", 0, 20, 3),
      sliderInput("n_bins3", "Bins in Third Plot", 0, 20, 3),
      position = "left"
  ),
  nav_panel(
    title = "Page 1",
    card(
      class = "p-0",
      layout_sidebar(
        sidebar = sidebar(
          "First card sidebar",
          open = list(desktop = "always", mobile = "open")
        ),
        plotOutput("plot")
      )
    ),
    card(
      class = "p-0",
      layout_sidebar(
        sidebar = sidebar(
          "Second card sidebar",
          open = list(desktop = "always", mobile = "closed")
        ),
        plotOutput("plot2")
      )
    ),
    card(
      class = "p-0",
      layout_sidebar(
        sidebar = sidebar(
          "Third card sidebar",
          open = list(desktop = "closed", mobile = "always")
        ),
        plotOutput("plot3")
      )
    )
  ),
  nav_panel(
    title = "Page flow",
    padding = 0,
    class = "bslib-flow-sm",
    card(lorem::ipsum(4, 2)),
    card(lorem::ipsum(4, 2))
  ),
  nav_panel(
    title = "Page sidebar",
    padding = 0,
    layout_sidebar(
      # class = "bslib-flow-sm",
      style = css("padding-left" = "var(--_padding-icon)"),
      sidebar = sidebar("My page-level sidebar"),
      card(lorem::ipsum(4, 2)),
      card(lorem::ipsum(4, 2))
    )
  )
)

server <- function(input, output, session) {
    x <- rnorm(1000, sd = 3)
    y <- rnorm(1000, sd = 10)
    primary <- bs_get_variables(bs_theme(), "primary")

    output$plot <- renderPlot({
        hist(x, breaks = input$n_bins %||% 5, col = primary)
    })

    output$plot2 <- renderPlot({
        hist(y, breaks = input$n_bins2 %||% 5, col = primary)
    })

    output$plot3 <- renderPlot({
        hist(y, breaks = input$n_bins3 %||% 5, col = primary)
    })
}

shinyApp(ui, server)

R/sidebar.R Outdated Show resolved Hide resolved
NAMESPACE Outdated Show resolved Hide resolved
@gadenbuie gadenbuie force-pushed the feat/sidebar-open-mobile-desktop branch from 048bed4 to 18372a7 Compare December 18, 2023 20:47
R/sidebar.R Outdated Show resolved Hide resolved
R/sidebar.R Outdated Show resolved Hide resolved
R/sidebar.R Outdated Show resolved Hide resolved
R/sidebar.R Show resolved Hide resolved
@gadenbuie gadenbuie requested a review from cpsievert December 19, 2023 21:23
R/sidebar.R Outdated Show resolved Hide resolved
NEWS.md Outdated Show resolved Hide resolved
@gadenbuie gadenbuie merged commit 3140888 into main Dec 20, 2023
13 checks passed
@gadenbuie gadenbuie deleted the feat/sidebar-open-mobile-desktop branch December 20, 2023 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

layout_sidebar() on mobile devices
2 participants