RinteRface / shinydashboardPlus

extensions for shinydashboard
https://shinydashboardplus.rinterface.com
Other
454 stars 77 forks source link

Expose a callback to shiny when a boxPlus is closed #69

Closed daattali closed 3 years ago

daattali commented 4 years ago

When boxPlus(closable=TRUE), an X icon appears that allows the user to close the box. It would be very helpful if this event generated a callback to shiny so that we can know when that happens.

I saw some discussion of this in #10 but that issue is for something else so I wanted to open a dedicated issue for this.

daattali commented 4 years ago

Also, what do you think of actually deleting the DOM of the box rather than just hiding it? Since there's no way to "unhide" a closed box, the current behaviour means that after closing a box, we're unable to add an input/output with an ID that existed in the box.

DivadNojnarg commented 4 years ago

In bs4Dash, I implemented an input binding to listen to the box state (which you can toggle, maximize, remove, ...). The adminlte2 JS API has also some of these methods. Quite easy to to: https://github.com/rstudio/shinydashboard/blob/master/srcjs/AdminLTE/app.js#L714

I can provide an input returning the list of the box state that is whether the box is currently closed, collapsed, ... I am also quite surprised that according to the adminLTE API, deleting a box simply hides it.

daattali commented 4 years ago

That is definitely surprising about the adminLTE API, that seems suboptimal.

I'm not familiar enough with all the states a box can have in order to form a strong opinion on what info exactly should be sent to shiny about a box. I just know that it can be useful to know when it closes. Whether it's a binary "isDeleted->true" that's sent to shiny or a more generic "state->deleted" that's up to you :)

DivadNojnarg commented 4 years ago

For now with 69b215f:

library(shiny)
library(shinydashboard)
library(shinydashboardPlus)

ui <- dashboardPagePlus(
  dashboardHeaderPlus(),
  dashboardSidebar(),
  dashboardBody(
    tags$style("body { background-color: ghostwhite}"),
    fluidRow(
      actionButton("toggle_box", "Toggle Box"),
      actionButton("remove_box", "Remove Box", class = "bg-danger"),
      actionButton("restore_box", "Restore Box", class = "bg-success")
    ),
    br(),
    boxPlus(
      title = textOutput("box_state"),
      "Box body",
      inputId = "mybox",
      collapsible = TRUE,
      closable = TRUE,
      plotOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  output$plot <- renderPlot({
    req(!input$mybox$collapsed)
    plot(rnorm(200))
  })

  output$box_state <- renderText({
    state <- if (input$mybox$collapsed) "collapsed" else "uncollapsed"
    paste("My box is", state)
  })

  observeEvent(input$toggle_box, {
    updateBoxPlus("mybox", action = "toggle")
  })

  observeEvent(input$remove_box, {
    updateBoxPlus("mybox", action = "remove")
  })

  observeEvent(input$restore_box, {
    updateBoxPlus("mybox", action = "restore")
  })

  observeEvent(input$mybox$visible, {
    collapsed <- if (input$mybox$collapsed) "collapsed" else "uncollapsed"
    visible <- if (input$mybox$visible) "visible" else "hidden"
    message <- paste("My box is", collapsed, "and", visible)
    showNotification(message, type = "warning", duration = 1500)
  })

}

shinyApp(ui, server)
DivadNojnarg commented 4 years ago

I am also thinking about removing the animations for toggle and remove since they add unnecessary delay to transmit the input value to shiny (550 ms is noticeable). Setting $.AdminLTE.boxWidget.animationSpeed = 10 looks much more reasonable.

daattali commented 4 years ago

That sounds like a reasonable update, yes half a second is a bit much