Search code examples
rshiny

Trying to resize page lengths in shiny app dynamically- not implementing


I'm trying to dynamically adjust the height of a datatable in the sidebar dependent on height of main content. So I've implemented a javascript script to address this. The datatable in the sidebar should show more or less rows to take up the length defined by the main content - which changes dependent on the page selected.

It seems the javascript is implementing but the page length is not dynamically changing. The sidebar extends way beyond the length of the main page content.

Is there a way to achieve this without using javascript?

app.R

library(tidyverse)
library(shiny)
library(shiny.fluent)
library(highcharter)
library(imola)
library(shinyjs)
library(shinyWidgets)
library(shiny.router)

# ---- test data
data <- data.frame(
  date = seq(as.Date("2022/01/01"), by = "month", length.out = 12),
  sales = c(10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32)
)

# ---- test data for sidebar
side_data <- data.frame(
  id = 1:1000,
  age = sample(18:80, 100, replace = TRUE),
  income = round(rnorm(100, mean = 50000, sd = 15000), 2),
  satisfaction = sample(1:5, 100, replace = TRUE),
  is_customer = sample(c(TRUE, FALSE), 100, replace = TRUE)
)

# ---- cols to show in sidebar table
details_list_columns <- tibble(
  fieldName = c("is_customer"),
  name = c("is_customer"),
  key = fieldName
)


# ---- create sidebar ----

# Custom rendering of group headers
navigation_styles <- list(
  root = list(
    height = "100%",
    width = "100%",
    boxSizing = "border-box"
  )
)

link_groups <- list(
  list(
    name = "",
    links = list(
      list(name = "A ", url =  route_link("/"), key= 'key1', icon = 'Home'),
      list(name = "B", url = route_link("page2"), key = 'key3', icon = 'GitGraph')
    )
  )
)


app_sidebar <- div(
  id = "sidebar",
  br(),
  Nav(
    groups = link_groups,
    selectedKey = "",
    styles = navigation_styles,
    onRenderGroupHeader = JS("group => React.createElement('h5', null, group.name)")
  ),
  Separator("Data"),
  div(
    id = "table",
    style = "overflow: auto;",
    uiOutput("table")
  )
)

# ---- create main content pages

page1 <- function(){
  div(
    id = "content",
    div(
      class = "area1",
      div(
        div(
          div(
            gridPage(
              actionButton("someButton", "Click Me")
            ),
            gridPage(
              materialSwitch(inputId = "switch", label = "", value = FALSE)
            )
          )
        ),
        div(
          class ="area2",
          highchartOutput("chart")
          )
        )
      )
    )
}

page2 <- function(){
  div(
    id = "content",
    div(
      class = "area1",
      div(
        div(
          div(
            gridPage(
              actionButton("someButton", "Click Me")
            )
          )
        ),
        div(
          class ="area2",
          p("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id cursus urna. Morbi feugiat pulvinar sem et sollicitudin. In id orci posuere, eleifend leo eget, malesuada mauris. Integer eu tincidunt nibh. Aenean gravida sapien eget magna commodo tempus nec sed purus. Fusce faucibus velit et leo ullamcorper tempus. Sed pretium enim odio, sed aliquet turpis faucibus eu. Integer quam enim, facilisis non ligula sed, feugiat fringilla ante. Integer consectetur, ex interdum fermentum suscipit, magna dolor scelerisque est, sed mattis lorem nibh vitae metus. Aenean venenatis iaculis neque, in sagittis libero elementum ut. Vivamus nec felis arcu. Maecenas quis dapibus arcu. Curabitur consectetur nulla eu elit tempus posuere. Nunc semper vulputate urna vitae ullamcorper.

Nulla tristique non sapien nec ultrices. Sed lacinia nunc a enim mattis, id pulvinar elit rutrum. Cras ornare ligula ut venenatis sodales. Maecenas ac nulla dui. Duis consectetur dolor nisi, vel ornare turpis porttitor ut. Vivamus pharetra eros eu tellus malesuada tristique. Curabitur eros quam, aliquet non egestas vitae, mollis sit amet augue. Cras gravida tristique arcu sed aliquam. Praesent posuere neque at odio hendrerit, eget lobortis ligula dapibus. Donec nec ex magna. Nam ut tempor elit. Phasellus hendrerit massa nec erat cursus, sit amet ornare est sagittis. In hac habitasse platea dictumst.

Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate.

Sed cursus elit id mauris luctus, et lacinia ligula eleifend. Aliquam erat volutpat. Aliquam ut neque sit amet justo molestie blandit id nec massa. Aliquam eu mauris a dui sollicitudin eleifend. Cras placerat eros nec lacus malesuada interdum. Duis ut odio aliquet, finibus purus ac, lobortis risus. Proin at sapien sit amet nunc tincidunt porta eu sit amet risus.

Praesent ut enim sagittis, fermentum erat et, facilisis eros. Nam massa diam, posuere nec lobortis egestas, congue eu lacus. Nulla facilisi. In pretium gravida elit vitae sodales. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur tempus nibh vel convallis consectetur. Aenean tristique tempor pulvinar. Cras eu congue diam. Vestibulum nec enim urna. In fermentum velit vitae felis hendrerit tincidunt sed sit amet ante. Duis faucibus metus id libero rhoncus ornare eget ut dolor. Nullam eleifend libero mi, quis maximus augue facilisis non. Phasellus dapibus scelerisque pharetra. Nulla ultrices, sem a consectetur pellentesque, lorem magna fermentum massa, non dignissim tellus urna a enim. Integer nulla nisl, eleifend ac nisl et, vestibulum commodo dolor.
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id cursus urna. Morbi feugiat pulvinar sem et sollicitudin. In id orci posuere, eleifend leo eget, malesuada mauris. Integer eu tincidunt nibh. Aenean gravida sapien eget magna commodo tempus nec sed purus. Fusce faucibus velit et leo ullamcorper tempus. Sed pretium enim odio, sed aliquet turpis faucibus eu. Integer quam enim, facilisis non ligula sed, feugiat fringilla ante. Integer consectetur, ex interdum fermentum suscipit, magna dolor scelerisque est, sed mattis lorem nibh vitae metus. Aenean venenatis iaculis neque, in sagittis libero elementum ut. Vivamus nec felis arcu. Maecenas quis dapibus arcu. Curabitur consectetur nulla eu elit tempus posuere. Nunc semper vulputate urna vitae ullamcorper.

Nulla tristique non sapien nec ultrices. Sed lacinia nunc a enim mattis, id pulvinar elit rutrum. Cras ornare ligula ut venenatis sodales. Maecenas ac nulla dui. Duis consectetur dolor nisi, vel ornare turpis porttitor ut. Vivamus pharetra eros eu tellus malesuada tristique. Curabitur eros quam, aliquet non egestas vitae, mollis sit amet augue. Cras gravida tristique arcu sed aliquam. Praesent posuere neque at odio hendrerit, eget lobortis ligula dapibus. Donec nec ex magna. Nam ut tempor elit. Phasellus hendrerit massa nec erat cursus, sit amet ornare est sagittis. In hac habitasse platea dictumst.

Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate.

Sed cursus elit id mauris luctus, et lacinia ligula eleifend. Aliquam erat volutpat. Aliquam ut neque sit amet justo molestie blandit id nec massa. Aliquam eu mauris a dui sollicitudin eleifend. Cras placerat eros nec lacus malesuada interdum. Duis ut odio aliquet, finibus purus ac, lobortis risus. Proin at sapien sit amet nunc tincidunt porta eu sit amet risus.

Praesent ut enim sagittis, fermentum erat et, facilisis eros. Nam massa diam, posuere nec lobortis egestas, congue eu lacus. Nulla facilisi. In pretium gravida elit vitae sodales. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur tempus nibh vel convallis consectetur. Aenean tristique tempor pulvinar. Cras eu congue diam. Vestibulum nec enim urna. In fermentum velit vitae felis hendrerit tincidunt sed sit amet ante. Duis faucibus metus id libero rhoncus ornare eget ut dolor. Nullam eleifend libero mi, quis maximus augue facilisis non. Phasellus dapibus scelerisque pharetra. Nulla ultrices, sem a consectetur pellentesque, lorem magna fermentum massa, non dignissim tellus urna a enim. Integer nulla nisl, eleifend ac nisl et, vestibulum commodo dolor.
            Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate.")
        )
      )
    )
  )
}

ui <- gridPage(
  
  useShinyjs(),
  
  tags$head(
    
    # tags$link(rel = "stylesheet", type = "text/css", href = "styles.css"),#
    # tags$script(src = "https://code.jquery.com/jquery-3.5.1.min.js"),
    # tags$script(src = "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"),#
    tags$script(src = "scripts.js")
    
  ),

 # define grid layout using imola 
 template = "grail-left-sidebar",
 gap = "0.3vw",
 rows = list(default = "50px 1fr 50px"), # 70px 1fr 30px
 
 
 sidebar = div(
   id = "sidebar",
   class = "imola-grid-item",
   app_sidebar),
 
 content = div(
   id = "content",
   class = "imola-grid-item",
   router_ui(
     route("/", page1()),
     route("page2", page2())

   )
 )
)


server <- (function(input, output, session) {
  
  shiny.router::router_server()
  
  # Send message to Shiny #
  observeEvent(input$someButton, {
    print("Button clicked!")
    # Example of sending message to client
    session$sendCustomMessage("buttonClicked", TRUE)
  })
  
  # Initialize contentHeight
  contentHeight <- reactiveVal(1000)
  
  # Update contentHeight when it changes
  observe({
    req(input$content_height)
    new_height <- input$content_height
    current_height <- new_height
    print(paste("Server: New height from input:", new_height, "Current height:", current_height))
    if (new_height != current_height) {
      contentHeight(new_height)
      print(paste("Height updated to:", new_height))
    }
  })
  
  output$table <- renderUI({
    height <- contentHeight()
    print(paste("Rendering table with height:", height))
    
    tryCatch({
      # Calculate available height
      available_height <- height - 50
      print(paste("Available height:", available_height))
      
      # Calculate max rows
      row_height <- 35
      max_rows <- max(1, floor(available_height / row_height))
      print(paste("Max rows:", max_rows))
      
      # Get your data
      data <- side_data
      
      # Limit the number of rows
      limited_data <- head(data, max_rows)
      
      DetailsList(
        style = list(height = sprintf("%fpx", available_height), overflowY = "auto"),
        items = limited_data,
        columns = details_list_columns,
        checkboxVisibility = 2,
        constrainMode = 0,
        compact = TRUE
      )
    }, error = function(e) {
      print(paste("Error in renderUI:", e$message, e$call))
      div("An error occurred while loading the data. Please try again later.")
    })
  })
  
  # Check current contentHeight
  observe({
    print(paste("Current contentHeight:", contentHeight()))
  })
  
  
  # draw charts
  output$chart <- renderHighchart({
    
    data <- data
    
    if (length(input$switch) == 0) {
      stop("input$switch is not initialized")
    }
    
    if (input$switch == FALSE) {
      print("Rendering line graph")
      highchart() %>%
        hc_xAxis(categories = data$date) %>%
        hc_add_series(data$sales, type = "line")
    } else {
      print("Graph Off")
      highchart() %>%
        hc_xAxis(categories = data$date) %>%
        hc_add_series(data$sales, type = "bar")
    }
  })
  
})

# Run the Shiny app
shinyApp(ui = ui, server = server)

scripts.js

 $(document).ready(function() {
  console.log("Document ready");
  
  // Shiny connected event
  $(document).on("shiny:connected", function() {
    console.log("Shiny connected");
    
    // Example: Receive message from Shiny server
    Shiny.addCustomMessageHandler("buttonClicked", function(message) {
      console.log("Received message from Shiny server:", message);
      alert("Button was clicked in Shiny!");
    });
    
    // Example: Send message to Shiny server
    $("#someButton").on("click", function() {
      Shiny.setInputValue("buttonClicked", true);
    });
    
    function updateContentHeight() {
      var contentHeight = $('#content').outerHeight();
      console.log('JavaScript: Content height = ' + contentHeight);
      if (typeof Shiny !== 'undefined' && typeof Shiny.setInputValue === 'function') {
        Shiny.setInputValue('content_height', contentHeight);
      }
      console.log("Height Updated")
    }

    // Example: Update height when tabs change
    $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
      setTimeout(updateContentHeight, 100);
    });

    // Initial update
    updateContentHeight();
  });
});

Solution

  • First we set height: max-content; to the content inside the main panel, this restricts the height of the content to be 'not larger than needed'. This height defines how long the sidebar content can be.

    Furthermore, you currently have in your sidebar multiple things displayed, e.g. a shiny.fluent::Nav(), a shiny.fluent::Separator() and the table. We attach a ResizeObserver to the content (this triggers e.g. when the user changes to page two) which sets the table height to the height of the content minus the height sum of all elements in the sidebar apart from the table.

    enter image description here

    library(shiny)
    library(shiny.fluent)
    library(highcharter)
    library(imola)
    library(shinyWidgets)
    library(shiny.router)
    library(tibble)
    
    # ---- test data
    data <- data.frame(
      date = seq(as.Date("2022/01/01"), by = "month", length.out = 12),
      sales = c(10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32)
    )
    
    # ---- test data for sidebar
    side_data <- data.frame(
      id = 1:1000,
      age = sample(18:80, 100, replace = TRUE),
      income = round(rnorm(100, mean = 50000, sd = 15000), 2),
      satisfaction = sample(1:5, 100, replace = TRUE),
      is_customer = sample(c(TRUE, FALSE), 100, replace = TRUE)
    )
    
    # ---- cols to show in sidebar table
    details_list_columns <- tibble(
      fieldName = c("is_customer"),
      name = c("is_customer"),
      key = fieldName
    )
    
    
    # ---- create sidebar ----
    
    # Custom rendering of group headers
    navigation_styles <- list(root = list(
      height = "100%",
      width = "100%",
      boxSizing = "border-box"
    ))
    
    link_groups <- list(list(name = "",
                             links = list(
                               list(
                                 name = "A ",
                                 url =  route_link("/"),
                                 key = 'key1',
                                 icon = 'Home'
                               ),
                               list(
                                 name = "B",
                                 url = route_link("page2"),
                                 key = 'key3',
                                 icon = 'GitGraph'
                               )
                             )))
    
    
    app_sidebar <- div(
      id = "sidebar",
      div(style = "height: 30px;"),
      Nav(
        groups = link_groups,
        selectedKey = "",
        styles = navigation_styles,
        onRenderGroupHeader = JS("group => React.createElement('h5', null, group.name)")
      ),
      Separator("Data"),
      div(id = "table",
          style = "overflow: auto;",
          uiOutput("table"))
    )
    
    # ---- create main content pages
    
    page1 <- function() {
      div(id = "content",
          div(class = "area1",
              div(
                div(div(
                  gridPage(actionButton("someButton1", "Click Me")),
                  gridPage(materialSwitch(
                    inputId = "switch",
                    label = "",
                    value = FALSE
                  ))
                )),
                div(class = "area2",
                    highchartOutput("chart"))
              )))
    }
    
    page2 <- function() {
      div(id = "content",
          div(class = "area1",
              div(div(div(
                gridPage(actionButton("someButton2", "Click Me"))
              )),
              div(
                class = "area2",
                p(
                  "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id cursus urna. Morbi feugiat pulvinar sem et sollicitudin. In id orci posuere, eleifend leo eget, malesuada mauris. Integer eu tincidunt nibh. Aenean gravida sapien eget magna commodo tempus nec sed purus. Fusce faucibus velit et leo ullamcorper tempus. Sed pretium enim odio, sed aliquet turpis faucibus eu. Integer quam enim, facilisis non ligula sed, feugiat fringilla ante. Integer consectetur, ex interdum fermentum suscipit, magna dolor scelerisque est, sed mattis lorem nibh vitae metus. Aenean venenatis iaculis neque, in sagittis libero elementum ut. Vivamus nec felis arcu. Maecenas quis dapibus arcu. Curabitur consectetur nulla eu elit tempus posuere. Nunc semper vulputate urna vitae ullamcorper.
    
    Nulla tristique non sapien nec ultrices. Sed lacinia nunc a enim mattis, id pulvinar elit rutrum. Cras ornare ligula ut venenatis sodales. Maecenas ac nulla dui. Duis consectetur dolor nisi, vel ornare turpis porttitor ut. Vivamus pharetra eros eu tellus malesuada tristique. Curabitur eros quam, aliquet non egestas vitae, mollis sit amet augue. Cras gravida tristique arcu sed aliquam. Praesent posuere neque at odio hendrerit, eget lobortis ligula dapibus. Donec nec ex magna. Nam ut tempor elit. Phasellus hendrerit massa nec erat cursus, sit amet ornare est sagittis. In hac habitasse platea dictumst.
    
    Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate.
    
    Sed cursus elit id mauris luctus, et lacinia ligula eleifend. Aliquam erat volutpat. Aliquam ut neque sit amet justo molestie blandit id nec massa. Aliquam eu mauris a dui sollicitudin eleifend. Cras placerat eros nec lacus malesuada interdum. Duis ut odio aliquet, finibus purus ac, lobortis risus. Proin at sapien sit amet nunc tincidunt porta eu sit amet risus.
    
    Praesent ut enim sagittis, fermentum erat et, facilisis eros. Nam massa diam, posuere nec lobortis egestas, congue eu lacus. Nulla facilisi. In pretium gravida elit vitae sodales. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur tempus nibh vel convallis consectetur. Aenean tristique tempor pulvinar. Cras eu congue diam. Vestibulum nec enim urna. In fermentum velit vitae felis hendrerit tincidunt sed sit amet ante. Duis faucibus metus id libero rhoncus ornare eget ut dolor. Nullam eleifend libero mi, quis maximus augue facilisis non. Phasellus dapibus scelerisque pharetra. Nulla ultrices, sem a consectetur pellentesque, lorem magna fermentum massa, non dignissim tellus urna a enim. Integer nulla nisl, eleifend ac nisl et, vestibulum commodo dolor.
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id cursus urna. Morbi feugiat pulvinar sem et sollicitudin. In id orci posuere, eleifend leo eget, malesuada mauris. Integer eu tincidunt nibh. Aenean gravida sapien eget magna commodo tempus nec sed purus. Fusce faucibus velit et leo ullamcorper tempus. Sed pretium enim odio, sed aliquet turpis faucibus eu. Integer quam enim, facilisis non ligula sed, feugiat fringilla ante. Integer consectetur, ex interdum fermentum suscipit, magna dolor scelerisque est, sed mattis lorem nibh vitae metus. Aenean venenatis iaculis neque, in sagittis libero elementum ut. Vivamus nec felis arcu. Maecenas quis dapibus arcu. Curabitur consectetur nulla eu elit tempus posuere. Nunc semper vulputate urna vitae ullamcorper.
    
    Nulla tristique non sapien nec ultrices. Sed lacinia nunc a enim mattis, id pulvinar elit rutrum. Cras ornare ligula ut venenatis sodales. Maecenas ac nulla dui. Duis consectetur dolor nisi, vel ornare turpis porttitor ut. Vivamus pharetra eros eu tellus malesuada tristique. Curabitur eros quam, aliquet non egestas vitae, mollis sit amet augue. Cras gravida tristique arcu sed aliquam. Praesent posuere neque at odio hendrerit, eget lobortis ligula dapibus. Donec nec ex magna. Nam ut tempor elit. Phasellus hendrerit massa nec erat cursus, sit amet ornare est sagittis. In hac habitasse platea dictumst.
    
    Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate.
    
    Sed cursus elit id mauris luctus, et lacinia ligula eleifend. Aliquam erat volutpat. Aliquam ut neque sit amet justo molestie blandit id nec massa. Aliquam eu mauris a dui sollicitudin eleifend. Cras placerat eros nec lacus malesuada interdum. Duis ut odio aliquet, finibus purus ac, lobortis risus. Proin at sapien sit amet nunc tincidunt porta eu sit amet risus.
    
    Praesent ut enim sagittis, fermentum erat et, facilisis eros. Nam massa diam, posuere nec lobortis egestas, congue eu lacus. Nulla facilisi. In pretium gravida elit vitae sodales. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur tempus nibh vel convallis consectetur. Aenean tristique tempor pulvinar. Cras eu congue diam. Vestibulum nec enim urna. In fermentum velit vitae felis hendrerit tincidunt sed sit amet ante. Duis faucibus metus id libero rhoncus ornare eget ut dolor. Nullam eleifend libero mi, quis maximus augue facilisis non. Phasellus dapibus scelerisque pharetra. Nulla ultrices, sem a consectetur pellentesque, lorem magna fermentum massa, non dignissim tellus urna a enim. Integer nulla nisl, eleifend ac nisl et, vestibulum commodo dolor.
                Aliquam congue dolor ac urna finibus, sed faucibus diam rhoncus. Curabitur efficitur augue at lacinia consequat. Vestibulum commodo commodo leo ac mattis. Praesent lobortis, nisi sed suscipit maximus, neque eros congue sem, sed sollicitudin elit dolor eget dolor. Vivamus vitae molestie ante. Duis odio lorem, commodo vitae velit et, luctus consectetur massa. Phasellus ultrices blandit aliquet. Nullam non odio a lorem interdum accumsan mollis in lectus. Donec diam nunc, euismod sed augue nec, rutrum viverra eros. Etiam dui mi, elementum nec sem eget, sodales cursus felis. Pellentesque id bibendum turpis, ac vulputate lacus. Curabitur mollis ligula quis libero ultricies vulputate."
                )
              ))))
    }
    
    ui <- gridPage(
      tags$head(tags$script(
        HTML(
          c(
            "$(document).ready(function() {",
            "  let myObserver = new ResizeObserver(function() {",
            "    setTimeout(function() {",
            "      var sidebarHeightwithoutDetailsList = 0;",
            "      $('#sidebar').children().each(function (el) {",
            "        if ($(this).attr('id') != 'table') {",
            "          sidebarHeightwithoutDetailsList += $(this).height();",
            "        }",
            "        $('#table').height($('#content').height() - sidebarHeightwithoutDetailsList);",
            "      })",
            "    }, 100)",
            "  });",
            "  myObserver.observe(document.querySelector('#content'));",
            "})"
          )
        )
      )),
      
      # define grid layout using imola
      template = "grail-left-sidebar",
      gap = "0.3vw",
      rows = list(default = "50px 1fr 50px"),
      # 70px 1fr 30px
      
      sidebar = div(id = "imolaSidebar",
                    class = "imola-grid-item",
                    app_sidebar),
      
      content = div(
        id = "content",
        style = "height: max-content;",
        class = "imola-grid-item",
        router_ui(route("/", page1()),
                  route("page2", page2()))
      )
    )
    
    server <- (function(input, output, session) {
      shiny.router::router_server()
      
      # Send message to Shiny #
      observeEvent(list(input$someButton1, input$someButton2), {
        print("Button clicked!")
        # Example of sending message to client
        session$sendCustomMessage("buttonClicked", TRUE)
      }, ignoreInit = TRUE)
      
      output$table <- renderUI({
        DetailsList(
          items = side_data,
          columns = details_list_columns,
          checkboxVisibility = 2,
          constrainMode = 0,
          compact = TRUE
        )
      })
      
      # draw charts
      output$chart <- renderHighchart({
        
        if (length(input$switch) == 0) {
          stop("input$switch is not initialized")
        }
        
        if (input$switch == FALSE) {
          highchart() %>%
            hc_xAxis(categories = data$date) %>%
            hc_add_series(data$sales, type = "line")
        } else {
          print("Graph Off")
          highchart() %>%
            hc_xAxis(categories = data$date) %>%
            hc_add_series(data$sales, type = "bar")
        }
      })
      
    })
    
    # Run the Shiny app
    shinyApp(ui = ui, server = server)