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

fix: Schedule .modal("hide") for transitioning modals #4173

Merged
merged 7 commits into from
Dec 31, 2024

Conversation

gadenbuie
Copy link
Member

@gadenbuie gadenbuie commented Dec 31, 2024

Fixes posit-dev/py-shiny#1318 for real.

The core issue is that calling $el.modal('hide') on a modal element $el that hasn't been fully revealed yet doesn't work. In practice, this means that if a modal is shown and hidden within 300ms (the default modal transition time) the .modal('hide') call silently does nothing.

This solution in this PR is to use the show.bs.modal event that fires at the start of the modal transition to track modals that are currently in a state of transition. When the shown.bs.modal event fires, we remove the modal from the set.

Separately, for removeModal() calls, if we receive a remove modal message that intends to remove a modal currently in the set of transitioning modals, then rather than calling .modal('hide') directly, we instead schedule to it to be called when the shown.bs.modal event occurs. This avoids any kind of race condition and guarantees that the current modal will be hidden as soon as the transition is complete. (From a UI perspective, this also avoids jarring transitions.)

Example app:

library(shiny)

run_model <- function(delay=10.0) {
  start_time <- Sys.time()
  while (difftime(Sys.time(), start_time, units="secs") < delay) {
    # do nothing, just wait
  }
  return(Sys.time())
}

ui <- fluidPage(
  theme = bslib::bs_theme(
    version = 5,
    preset = "shiny",
    # The default transition is 0.3s, try increasing to 3s
    "modal-transition" = "transform 0.3s ease-out"
  ),
  actionButton("run", "Run Model"),
  textOutput("results")
)

server <- function(input, output, session) {
  v_results <- reactiveVal()

  observeEvent(input$run, {
    showModal(modalDialog(
      title = "Running model",
      "The model is running, please wait.",
      footer = NULL,
      easyClose = FALSE
    ))

    # adjust the delay in seconds down below the modal transition time (above)
    results <- run_model(delay=1)

    v_results(results)
    removeModal()
  })

  output$results <- renderText({
    v_results()
  })
}

shinyApp(ui, server)

Copy link
Collaborator

@schloerke schloerke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM given the two calls are made at the same time, removing the WeakSet usage.

srcts/src/shiny/modal.ts Outdated Show resolved Hide resolved
@gadenbuie gadenbuie merged commit 13ca8df into main Dec 31, 2024
1 check passed
@gadenbuie gadenbuie deleted the fix/quick-show-hide-modal branch December 31, 2024 21:27
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.

ui.modal_remove fails unless fade is set to False
2 participants