rshinydashboardbs4dash

How to remember last active tabs in a multiple tabset setup?


Is there a more native way of remembering the last active tab, when changing tabsets (here in header / navbar), to return to it upon tabset change in bs4Dash?

library(shiny)
library(bs4Dash)
library(shinyjs)

ui <- bs4DashPage(
  header = bs4DashNavbar(
    title = "Remember Last Tab in bs4Dash",
    tags$div(
      id = "tabset-container",
      style = "display: flex; justify-content: center; gap: 10px; padding: 10px;",
      div(
        id = "tabset1",
        style = "background-color: #0073B7; padding: 10px; color: white; cursor: pointer;", "Tab Set 1"
      ),
      div(
        id = "tabset2",
        style = "background-color: #0073b7; padding: 10px; color: white; cursor: pointer;", "Tab Set 2"
      )
    )
  ),
  sidebar = bs4DashSidebar(
    uiOutput("sidebar_menu")
  ),
  body = bs4DashBody(
    useShinyjs(),
    tags$script(HTML("
        $(document).on('shiny:connected', function (event) {
      Shiny.setInputValue('activeTabSet', 'tabset1')
      })

    Shiny.addCustomMessageHandler('selectTab', function(tabName) {
      var sidebar = document.querySelector('[data-widget=\"treeview\"]');
      var tab = sidebar.querySelector('[data-value=\"' + tabName + '\"]'); 
      if (tab) tab.click(); 
    });

    Shiny.addCustomMessageHandler('tabsetReady', function(tabset) {
      setTimeout(function() {
        Shiny.setInputValue('tabsetReady', tabset, {priority: 'event'});
      }, 100);
    });
  ")),
    bs4TabItems(
      bs4TabItem(tabName = "tab1_1", h2("Content for Tab 1.1")),
      bs4TabItem(tabName = "tab1_2", h2("Content for Tab 1.2")),
      bs4TabItem(tabName = "tab2_1", h2("Content for Tab 2.1")),
      bs4TabItem(tabName = "tab2_2", h2("Content for Tab 2.2"))
    )
  )
)

server <- function(input, output, session) {
  # Reactive values to store the last visited tab for each tabset
  lastTabs <- reactiveValues(tabset1 = "tab1_1", tabset2 = "tab2_1")

  shinyjs::onclick("tabset1", {
    runjs('Shiny.setInputValue("activeTabSet", "tabset1")')
  })

  shinyjs::onclick("tabset2", {
    runjs('Shiny.setInputValue("activeTabSet", "tabset2")')
  })

  # Dynamically render the sidebar menu based on the active tabset
  output$sidebar_menu <- renderUI({
    req(input$activeTabSet)
    if (input$activeTabSet == "tabset1") {
      bs4SidebarMenu(
        id = "sidebar",
        bs4SidebarMenuItem("Tab 1.1", tabName = "tab1_1", icon = icon("dashboard")),
        bs4SidebarMenuItem("Tab 1.2", tabName = "tab1_2", icon = icon("chart-bar"))
      )
    } else if (input$activeTabSet == "tabset2") {
      bs4SidebarMenu(
        id = "sidebar",
        bs4SidebarMenuItem("Tab 2.1", tabName = "tab2_1", icon = icon("globe")),
        bs4SidebarMenuItem("Tab 2.2", tabName = "tab2_2", icon = icon("cogs"))
      )
    }
  })

  observeEvent(input$sidebar, {
    req(input$activeTabSet)

    if (input$activeTabSet == "tabset1") {
      lastTabs$tabset1 <- input$sidebar
    } else if (input$activeTabSet == "tabset2") {
      lastTabs$tabset2 <- input$sidebar
    }
  })

  observeEvent(input$activeTabSet, {
    req(input$activeTabSet)
    session$sendCustomMessage("tabsetReady", input$activeTabSet)
  })

  observeEvent(input$tabsetReady, {
    req(input$tabsetReady)

    if (input$tabsetReady == "tabset1") {
      session$sendCustomMessage("selectTab", lastTabs$tabset1)
    } else if (input$tabsetReady == "tabset2") {
      session$sendCustomMessage("selectTab", lastTabs$tabset2)
    }
  })
}

shinyApp(ui, server)

Solution

  • Answer in "more native way", without extra javascript

    library(shiny)
    library(bs4Dash)
    
    
    ui <- bs4DashPage(
      header = bs4DashNavbar(
        title = "Remember Last Tab in bs4Dash",
        tags$div(
          id = "tabset-container",
          style = "display: flex; justify-content: center; gap: 10px; padding: 10px;",
          bs4Dash::actionButton("tabset1",
                                "Tab Set 1", 
                                style = "background-color: #0073b7; padding: 10px; color: white; cursor: pointer;"
          ),
          bs4Dash::actionButton("tabset2",
                                "Tab Set 2", 
                                style = "background-color: #0073b7; padding: 10px; color: white; cursor: pointer;"
          )
        )
      ),
      sidebar = bs4DashSidebar(
        uiOutput("sidebar_menu")
      ),
      body = bs4DashBody(
        bs4TabItems(
          bs4TabItem(tabName = "tab1_1", h2("Content for Tab 1.1")),
          bs4TabItem(tabName = "tab1_2", h2("Content for Tab 1.2")),
          bs4TabItem(tabName = "tab2_1", h2("Content for Tab 2.1")),
          bs4TabItem(tabName = "tab2_2", h2("Content for Tab 2.2"))
        )
      )
    )
    
    server <- function(input, output, session) {
      
      lastTabs <- reactiveValues(tabset1 = "tab1_1", 
                                 tabset2 = "tab2_1",
                                 activeTabSet="tabset1"
      )
      
      updateTab.fct <- function  (param_input, lastTabs_input) {
        if (!(is.null(param_input)) &&   param_input >0 ) {
          if (!is.null(lastTabs_input)) {
            
            
            updatebs4TabItems(
              session,
              inputId = "sidebar",
              selected =lastTabs_input[[lastTabs_input$activeTabSet]]
            )
          }
        }
      }
      observeEvent(input$tabset1, {
        lastTabs$activeTabSet <- "tabset1" 
        lastTabs$tabset1 <- lastTabs[[lastTabs$activeTabSet]]
        updateTab.fct(input$tabset1,lastTabs)
        
        
        
      }, ignoreInit = TRUE)
      
      observeEvent(input$tabset2, {
        lastTabs$activeTabSet <- "tabset2" 
        lastTabs$tabset2 <- lastTabs[[lastTabs$activeTabSet]]
        updateTab.fct(input$tabset2,lastTabs)
        
        
      }, ignoreInit = TRUE)
      
      observeEvent(input$sidebar, {
        
        if (is.null(input$sidebar) ) {
          #  lastTabs$activeTabSet <- "tabset1" 
          lastTabs$tabset1 <- lastTabs[[lastTabs$activeTabSet]]
          updateTab.fct(1,lastTabs)
        } else if (lastTabs$activeTabSet == "tabset1") {
          lastTabs$tabset1 <- input$sidebar
        } else if (lastTabs$activeTabSet == "tabset2") {
          lastTabs$tabset2 <- input$sidebar
        }
      }, ignoreInit = FALSE, ignoreNULL = FALSE)
      
      
      output$sidebar_menu <- renderUI({
        
        if (ifelse (is.null(lastTabs$activeTabSet),"tabset1", lastTabs$activeTabSet) == "tabset1") {
          
          bs4SidebarMenu(
            id = "sidebar",
            bs4SidebarMenuItem("Tab 1.1", tabName = "tab1_1", icon = icon("dashboard")),
            bs4SidebarMenuItem("Tab 1.2", tabName = "tab1_2", icon = icon("chart-bar"))
          )
        } else  {
          
          bs4SidebarMenu(
            id = "sidebar",
            bs4SidebarMenuItem("Tab 2.1", tabName = "tab2_1", icon = icon("globe")),
            bs4SidebarMenuItem("Tab 2.2", tabName = "tab2_2", icon = icon("cogs"))
          )
        }
      })
      
    }
    
    shinyApp(ui, server)