shinycssloaders icon indicating copy to clipboard operation
shinycssloaders copied to clipboard

Unable to see spinner when parent is using display: flex

Open JasonAizkalns opened this issue 1 year ago • 5 comments

Looks like something is happening to the visibility of the spinner when using d-flex and/or justify-content-center:

library(shiny)
library(shinycssloaders)
library(bslib)

tab <- function(...) {
  shiny::tabPanel(..., class = "p-3 border border-top-0 rounded-bottom")
}

ui <- page_navbar(
  nav_panel(
    "Tables",
    tabsetPanel(
      tab(
        "one",
        div(
          class = "d-flex justify-content-center", # comment this line and things work.
          div(
            withSpinner(verbatimTextOutput("text"))
          ) 
        )
      )
    )
  )
)

server <- function(input, output) {
  output$text <- renderText({
    Sys.sleep(2)
    "some text"
  })

}

shinyApp(ui, server)

JasonAizkalns avatar Sep 15 '23 16:09 JasonAizkalns

There's quite a lot going on here,- using page_navbar, nav_panel, custom classes on the tab, and finally the classes that are mentioned in the bug report.

Could you please reduce the reprex to the bare minimum? If there's an issue with d-flex and justify-content-center classes, then show a barebones shiny app with a spinner wrapped in those classes, with nothing else.

Thanks for the report, I'll take a look when the code is more minimal!

daattali avatar Sep 15 '23 23:09 daattali

Thanks @daattali -- here's a more stripped down example:

library(shiny)
library(shinycssloaders)
library(bslib)

ui <- bslib::page(
    div(
      class = "d-flex justify-content-center",
      div(withSpinner(verbatimTextOutput("text")))
    )
)

server <- function(input, output) {
  output$text <- renderText({
    Sys.sleep(2)
    "some text"
  })
  
}

shinyApp(ui, server)

Seems like just the introduction of d-flex is doing something. To avoid an x/y problem, what am I actually trying to do? I am trying to keep a reactable table in the "center" horizontally reactable(..., fullWidth = FALSE) while still using withSpinner and using bslib for theming (which means Bootstrap 5).

JasonAizkalns avatar Sep 18 '23 13:09 JasonAizkalns

Thanks - using your new information I was able to narrow it down to the fact that shinycssloaders doesn't work when it's inside a flexbox. Here's the minimal reprex I came up with:

library(shiny)

ui <- fluidPage(
    div(style="display: flex;", shinycssloaders::withSpinner(textOutput("text")))
)

server <- function(input, output) {
    output$text <- renderText({
        Sys.sleep(2)
        "some text"
    })
}

shinyApp(ui, server)

I have a feeling this might be a difficult one to solve correctly. I noticed that if I remove this one CSS line:

https://github.com/daattali/shinycssloaders/blob/62815f7af20df73e1dfd561ce9a9460f73907488/inst/assets/spinner.css#L2

then the spinner does show up. But I can't simply remove it, because that CSS rule was literally the first CSS rule used in this package, and all the features depend on it. I don't know what features or usecases might break if it's removed. Someone would need to do a lot of careful testing of different shinycssloaders examples to see if it's ok to remove that line, or alternatively if anything can be added to to allow flexboxes to work.

If you need this to work ASAP, you can fork this project, and remove that line, and if it works for your usecase then just use your forked version.

daattali avatar Sep 21 '23 02:09 daattali

Ah, understood. At least now we could 'hack' an ID and force its position to be static. FWIW, this works in the minimal examples:

library(shiny)
library(shinycssloaders)
library(bslib)

ui <- bslib::page(
  header = tags$style(HTML("
    #hack > .shiny-spinner-output-container {
      position: static;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  ")),
  div(
    class = "d-flex justify-content-center",
    div(id = "hack", withSpinner(verbatimTextOutput("text")))
  )
)

server <- function(input, output) {
  output$text <- renderText({
    Sys.sleep(2)
    "some text"
  })
  
}

shinyApp(ui, server)

I tried some things with conditional styling (and that might be a decent long-term approach), but wasn't successful. I'm thinking something close to this could work in spinner.css:

  div[style*='display: flex'] .shiny-spinner-output-container {
    position: static;
  }

JasonAizkalns avatar Sep 21 '23 13:09 JasonAizkalns

I'm not inclined to use a solution like that, not just because such CSS selectors are not performant, but it's also a little too hacky/unclean for my taste! I'd prefer finding a cleaner approach

daattali avatar Sep 22 '23 00:09 daattali