Search code examples
cssrshiny

How to force shiny apps to take up the whole length on mobile devices?


I created a website with several pages using R markdown.The first page just contains introductory text. The second page consists of four tabs, each for a shiny app (map or table).

It all works well on desktop. On mobile devices, the first page of text takes up the entire length of the phone - great. But the tabs with apps on them only take up a portion of the screen, within which you need to scroll, and I can't figure out why.

Min reproducible code:

---
title: "Climate Vulnerability and Readiness"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
runtime: shiny
editor_options: 
  markdown: 
    wrap: 72
---

<style>
.justified-text {
  text-align: justify;
  margin-left: 6em;
  margin-right: 6em;
}

/* Media query for mobile devices */
@media (max-width: 768px) {
  .justified-text {
    margin-left: 1em;
    margin-right: 1em;
    padding-top: 4em; 
  }
  .sidebar-panel, .main-panel {
    width: 100% !important;
    float: none !important;
    padding: 0 !important;
  }
  .tabset-panel {
    width: 100% !important;
  }
  .sidebar-layout {
    flex-direction: column;
  }
  .shiny-output-error {
    display: none;
  }
  .main-panel, .content {
    height: 100% !important;
    overflow: auto;
  }
  .titlePanel {
    padding-top: 4em; 
  }
  .shiny-bound-output {
    height: 100%;
  }
  }

/* Media query for desktop devices */
@media (min-width: 769px) {
  .main-panel {
    overflow: auto;
  }
}
</style>

# Introduction {.tabset}
<div class="justified-text">

[Introductory text]

</div>

# Analysis {.tabset}

```{r}
library(shiny)
library(googleVis)
library(dplyr)
library(DT)

# Load your data
combined_vul_data <- 
 structure(list(ISO3 = c("AF", "AL", "DZ", "AD", "AO", "AG", "AR", 
"AM", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", 
"BJ", "BT"), Name = c("Afghanistan", "Albania", "Algeria", "Andorra", 
"Angola", "Antigua and Barbuda", "Argentina", "Armenia", "Australia", 
"Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", 
"Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bhutan"
), vul_capacity_Rank_2021 = c(0.709035449, 0.511126182, 0.523332101, 
NA, 0.740026186, 0.621133795, 0.446088596, 0.462491218, 0.296565054, 
0.26689445, 0.47917568, NA, 0.453936004, 0.649876424, 0.398904506, 
0.386808093, 0.27763708, 0.547525366, 0.763503499, 0.635633065
)), class = "data.frame", row.names = c(NA, -20L))

combined_readi_data <- structure(list(ISO3 = c("AF", "AL", "DZ", "AD", "AO", "AG", "AR", 
"AM", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", 
"BJ", "BT"), Name = c("Afghanistan", "Albania", "Algeria", "Andorra", 
"Angola", "Antigua and Barbuda", "Argentina", "Armenia", "Australia", 
"Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", 
"Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bhutan"
), readi_readiness = c(0.246442145333371, 0.410559121234659, 
0.33334509314068, 0.478714965470705, 0.268117798333208, 0.449260130429268, 
0.376037316185072, 0.50573276439272, 0.690552124980166, 0.68230738466746, 
0.447625682772037, 0.427831425066084, 0.519334145918882, 0.281041117917878, 
0.53612684461446, 0.490112049917615, 0.600207432355383, 0.333388191619152, 
0.337878182675485, 0.483057133272884)), class = "data.frame", row.names = c(NA, 
-20L))


# Define UI for the map and table
ui <- fluidPage(
  tags$head(
    tags$style(HTML("
      .dataTables_wrapper .dataTables_filter {
        float: left !important;
        text-align: left !important;
      }
      .dataTables_wrapper .dataTables_filter input {
        margin-left: 0.5em;
        width: 200px;
      }
      .table-container {
        display: flex;
        flex-direction: row;
      }
      .table-container .table-controls {
        margin-right: 2em;
      }
      @media (max-width: 768px) {
        .table-container {
          flex-direction: column;
        }
        .table-container .table-controls {
          margin-right: 0;
          margin-bottom: 1em;
        }
        .shiny-input-container {
          width: 100%;
        }
        .dataTables_wrapper .dataTables_filter input {
          width: 100%;
        }
        .main-panel, .content {
          height: calc(100vh - 56px) !important;
          overflow: auto;
        }
        .main-panel {
          height: 100vh;
        }
      }
    "))
  ),
  titlePanel(div(span("Country Rankings"), style={'padding-top: 15px'})),
  tabsetPanel(
    tabPanel("Vulnerability Map",
      sidebarLayout(
        sidebarPanel(
          selectInput("year", "Select Year", choices = 2021, selected = 2021),
          selectInput("category", "Select Category", 
                      choices = list("Adaptive Capacity" = "vul_capacity"), selected = "vul_vulnerability"),
          textOutput("category_context")
        ),
        mainPanel(
          htmlOutput("map")
        )
      )
    ),
    tabPanel("Vulnerability Table",
      sidebarLayout(
        sidebarPanel(
          selectInput("table_year", "Select Year", choices = 2021, selected = 2021),
          selectInput("table_category", "Select Category", 
                      choices = list("Adaptive Capacity" = "vul_capacity"), selected = "vul_capacity")
        ),
        mainPanel(
          DTOutput("vulnerability_table")
        )
      )
    ),
    tabPanel("Readiness Map",
      sidebarLayout(
        sidebarPanel(
          selectInput("readiness_year", "Select Year", choices = 2021, selected = 2021),
          selectInput("readiness_category", "Select Category", 
                      choices = list("Overall Readiness" = "readi_readiness"), selected = "readi_readiness"),
          textOutput("readiness_category_context")
        ),
        mainPanel(
          htmlOutput("readiness_map")
        )
      )
    ),
    tabPanel("Readiness Table",
      sidebarLayout(
        sidebarPanel(
          selectInput("readiness_table_year", "Select Year", choices = 1995:2021, selected = 2021),
          selectInput("readiness_table_category", "Select Category", 
                      choices = list("Economic" = "readi_economic", 
                                     "Governance" = "readi_governance", "Overall Readiness" = "readi_readiness",
                                     "Social" = "readi_social"), selected = "readi_readiness")
        ),
        mainPanel(
          DTOutput("readiness_table")
        )
      )
    )
  )
)

# Define server logic for the map and table
server <- function(input, output) {
  output$category_context <- renderText({
    context <- switch(input$category,
                      "vul_capacity" = "The availability of social resources for sector-specific adaptation. In some cases, these capacities reflect sustainable adaptation solutions. In other cases, they reflect capacities to put newer, more sustainable adaptations into place.", "")
    context
  })
  
  output$map <- renderGvis({
    year <- input$year
    category <- input$category
    
    # Create the column name for the selected year and category
    rank_column <- paste(category, "_Rank_", year, sep = "")
    
    # Filter and prepare the data
    map_data <- combined_vul_data %>%
      select(ISO3, Name, Rank = !!sym(rank_column)) %>%
      mutate(Tooltip = ifelse(is.na(Rank), paste(Name, "- Insufficient data"), paste(Name)))
    
    
    # Update locationvar column
    map_data <- map_data %>%
      mutate(Location = ifelse(Name %in% c("Congo (Brazzaville)", "Congo (Kinshasa)"), ISO3, Name))
    
    # Determine the range of the Rank values
    min_rank <- min(map_data$Rank, na.rm = TRUE)
    max_rank <- max(map_data$Rank, na.rm = TRUE)
    
    # Create the map
    gvisGeoChart(map_data, locationvar = "Location", colorvar = "Rank",
                 hovervar = "Tooltip",  
                 options = list(colorAxis = paste0("{minValue:", min_rank, ", maxValue:", max_rank, ", colors:['#00FF00', '#FFFF00', '#FF0000']}"),
                                backgroundColor = '#81d4fa', datalessRegionColor = '#f5f5f5',
                                defaultColor = '#f5f5f5',
                                tooltip = "{isHtml: true}",
                                width = "100%",
                                height = "500px"))  
  })

  output$vulnerability_table <- renderDT({
    year <- input$table_year
    category <- input$table_category

    # Prepare data for the table
    table_data <- prepare_table_data(combined_vul_data, year, category)

    # Calculate min and max rank
    min_rank <- min(as.numeric(table_data$Rank), na.rm = TRUE)
    max_rank <- max(as.numeric(table_data$Rank), na.rm = TRUE)

    # Generate a color scale
    color_scale <- colorRampPalette(c('#00FF00', '#FFFF00', '#FF0000'))(max_rank - min_rank + 1)

    # Render the interactive table
    datatable(table_data, options = list(
      dom = 'Bt',
      pageLength = -1, 
      autoWidth = TRUE, 
      scrollY = FALSE,
      columnDefs = list(
        list(targets = 0, className = 'dt-right')
      )
    ), 
    rownames = FALSE, 
    filter = list(position = 'top', clear = FALSE, plainText = TRUE), 
    escape = FALSE) %>%
      formatStyle('Rank', 
                  backgroundColor = styleEqual(seq(min_rank, max_rank), color_scale),
                  color = 'black')
  })

  output$readiness_category_context <- renderText({
    context <- switch(input$readiness_category,
                      "readi_readiness" = "Overall readiness measures a country's ability to leverage investments and convert them to adaptation actions. It combines economic, governance, and social readiness scores to provide a comprehensive measure.",
                      "")
    context
  })
  
  output$readiness_map <- renderGvis({
    year <- input$readiness_year
    category <- input$readiness_category
    
    # Create the column name for the selected year and category
    rank_column <- paste(category, "_Rank_", year, sep = "")
    
    # Filter and prepare the data
    map_data <- combined_readi_data %>%
      select(ISO3, Name, Rank = !!sym(rank_column)) %>%
      mutate(Location = ifelse(Name %in% c("Congo (Brazzaville)", "Congo (Kinshasa)"), ISO3, Name)) %>%
      mutate(Tooltip = ifelse(is.na(Rank), paste(Name, "- Insufficient data"), paste(Name)))
    
    # Determine the range of the Rank values
    min_rank <- min(map_data$Rank, na.rm = TRUE)
    max_rank <- max(map_data$Rank, na.rm = TRUE)
    
    # Create the map
    gvisGeoChart(map_data, locationvar = "Location", colorvar = "Rank",
                 hovervar = "Tooltip",  # Use the Tooltip column
                 options = list(colorAxis = paste0("{minValue:", min_rank, ", maxValue:", max_rank, ", colors:['#00FF00', '#FFFF00', '#FF0000']}"),
                                backgroundColor = '#81d4fa', datalessRegionColor = '#f5f5f5',
                                defaultColor = '#f5f5f5',
                                tooltip = "{isHtml: true}",
                                width = "100%",
                                height = "500px"))  
  })

  output$readiness_table <- renderDT({
    year <- input$readiness_table_year
    category <- input$readiness_table_category

    # Prepare data for the table
    table_data <- prepare_table_data_readiness(combined_readi_data, year, category)

    # Calculate min and max rank
    min_rank <- min(as.numeric(table_data$Rank), na.rm = TRUE)
    max_rank <- max(as.numeric(table_data$Rank), na.rm = TRUE)

    # Generate a color scale
    color_scale <- colorRampPalette(c('#00FF00', '#FFFF00', '#FF0000'))(max_rank - min_rank + 1)

    # Render the interactive table
    datatable(table_data, options = list(
      dom = 'Bt', 
      buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
      pageLength = -1, 
      autoWidth = TRUE, 
      scrollY = FALSE,
      columnDefs = list(
        list(targets = 0, className = 'dt-right')
      )
    ), 
    rownames = FALSE, 
    filter = list(position = 'top', clear = FALSE, plainText = TRUE), 
    escape = FALSE) %>%
      formatStyle('Rank', 
                  backgroundColor = styleEqual(seq(min_rank, max_rank), color_scale),
                  color = 'black')
  })
}

# Prepare data for the table
prepare_table_data <- function(data, year, category) {
  rank_column <- paste(category, "_Rank_", year, sep = "")
  
  data <- data %>%
    select(ISO3, Name, !!rank_column) %>%
    rename(Rank = !!rank_column) %>%
    mutate(Rank = ifelse(is.na(Rank), "Insufficient data", Rank)) %>%
    select(Rank, Country = Name) %>%
    arrange(as.numeric(Rank))
  
  return(data)
}

# Prepare data for the readiness table
prepare_table_data_readiness <- function(data, year, category) {
  rank_column <- paste(category, "_Rank_", year, sep = "")
  
  data <- data %>%
    select(ISO3, Name, !!rank_column) %>%
    rename(Rank = !!rank_column) %>%
    mutate(Rank = ifelse(is.na(Rank), "Insufficient data", Rank)) %>%
    select(Rank, Country = Name) %>%
    arrange(as.numeric(Rank))
  
  return(data)
}

shinyApp(ui = ui, server = server)
```

Solution

  • The reason why the whole length is not taken into account is that the whole content on the second tab is rendered into an iframe and this frame has a fixed height by default. In order to use the full view height you can add this CSS:

    .shiny-frame { 
      height: 100vh; 
    }
    

    Concerning the other point why the map is initially not fitting into the screen size it seems to be like this: You currently assign width = "100%" inside gvisGeoChart(). This 100% relate to an initial rendering inside a full screen mode. A possibility is of course to set fixed width here, however, this is maybe not a convenient solution.

    What generally would be the optimal solution is to trigger a re-rendering of the image, e.g. if you visit its tab. However, due to several Javascript topics I could not write a suitable event listener which could be used. Another option which is implemented below is that the user actively sets the dropdown value for e.g. the year (before the plot is not shown), this also works. You may find a better solution or ask a follow-up-question.

    ---
    title: "Climate Vulnerability and Readiness"
    output: 
      flexdashboard::flex_dashboard:
        orientation: columns
        vertical_layout: fill
    runtime: shiny
    editor_options: 
      markdown: 
        wrap: 72
    ---
    
    <style>
    .justified-text {
      text-align: justify;
      margin-left: 6em;
      margin-right: 6em;
    }
    
    /* Media query for mobile devices */
    @media (max-width: 768px) {
      .justified-text {
        margin-left: 1em;
        margin-right: 1em;
        padding-top: 4em; 
      }
      .sidebar-panel, .main-panel {
        width: 100% !important;
        float: none !important;
        padding: 0 !important;
      }
      .tabset-panel {
        width: 100% !important;
      }
      .sidebar-layout {
        flex-direction: column;
      }
      .shiny-output-error {
        display: none;
      }
      .main-panel, .content {
        height: 100% !important;
        overflow: auto;
      }
      .titlePanel {
        padding-top: 4em; 
      }
      .shiny-bound-output {
        height: 100%;
      }
      .shiny-frame {
        height: 100vh;
      }
    }
    
    /* Media query for desktop devices */
    @media (min-width: 769px) {
      .main-panel {
        overflow: auto;
      }
    }
    </style>
    
    # Introduction {.tabset}
    <div class="justified-text">
    
    As climate change impacts continue to unfold, countries around the world are grappling with the challenges of adapting. The magnitude of this struggle varies, with some nations more vulnerable due to their geographic locations or socio-economic conditions. Conversely, certain countries are better positioned to adapt—benefiting from more favorable conditions—and can leverage public and private sector investments, government initiatives, and community engagement to drive effective responses. Here, we take a look at how countries rank both in terms of how vulnerable and how ready they are to face climate change challenges.
    
    While every nation must confront the reality of climate change, the disparity in vulnerability and readiness underscores a critical issue of global inequality. Often, the countries most at risk are also the ones least equipped to respond effectively, lacking the necessary resources and infrastructure. We must raise awareness about this pressing inequality, highlighting the urgent need for a more equitable approach to climate resilience and adaptation support. It is imperative that we acknowledge and address the unfair burden placed on the most vulnerable nations.
    
    For more insight into the data used, please see this paper. The source data can be accessed here.
    
    As climate change impacts continue to unfold, countries around the world are grappling with the challenges of adapting. The magnitude of this struggle varies, with some nations more vulnerable due to their geographic locations or socio-economic conditions. Conversely, certain countries are better positioned to adapt—benefiting from more favorable conditions—and can leverage public and private sector investments, government initiatives, and community engagement to drive effective responses. Here, we take a look at how countries rank both in terms of how vulnerable and how ready they are to face climate change challenges.
    
    While every nation must confront the reality of climate change, the disparity in vulnerability and readiness underscores a critical issue of global inequality. Often, the countries most at risk are also the ones least equipped to respond effectively, lacking the necessary resources and infrastructure. We must raise awareness about this pressing inequality, highlighting the urgent need for a more equitable approach to climate resilience and adaptation support. It is imperative that we acknowledge and address the unfair burden placed on the most vulnerable nations.
    
    For more insight into the data used, please see this paper. The source data can be accessed here.
    
    As climate change impacts continue to unfold, countries around the world are grappling with the challenges of adapting. The magnitude of this struggle varies, with some nations more vulnerable due to their geographic locations or socio-economic conditions. Conversely, certain countries are better positioned to adapt—benefiting from more favorable conditions—and can leverage public and private sector investments, government initiatives, and community engagement to drive effective responses. Here, we take a look at how countries rank both in terms of how vulnerable and how ready they are to face climate change challenges.
    
    While every nation must confront the reality of climate change, the disparity in vulnerability and readiness underscores a critical issue of global inequality. Often, the countries most at risk are also the ones least equipped to respond effectively, lacking the necessary resources and infrastructure. We must raise awareness about this pressing inequality, highlighting the urgent need for a more equitable approach to climate resilience and adaptation support. It is imperative that we acknowledge and address the unfair burden placed on the most vulnerable nations.
    
    For more insight into the data used, please see this paper. The source data can be accessed here.
    
    </div>
    
    # Analysis {.tabset}
    
    ```{r}
    library(shiny)
    library(googleVis)
    library(dplyr)
    library(DT)
    
    combined_vul_data <- 
      structure(list(ISO3 = c("AF", "AL", "DZ", "AD", "AO", "AG", "AR", 
    "AM", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", 
    "BJ", "BT"), Name = c("Afghanistan", "Albania", "Algeria", "Andorra", 
    "Angola", "Antigua and Barbuda", "Argentina", "Armenia", "Australia", 
    "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", 
    "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bhutan"
    ), vul_capacity_Rank_2021 = c(0.709035449, 0.511126182, 0.523332101, 
    NA, 0.740026186, 0.621133795, 0.446088596, 0.462491218, 0.296565054, 
    0.26689445, 0.47917568, NA, 0.453936004, 0.649876424, 0.398904506, 
    0.386808093, 0.27763708, 0.547525366, 0.763503499, 0.635633065
    )), class = "data.frame", row.names = c(NA, -20L))
    
    combined_readi_data <- structure(list(ISO3 = c("AF", "AL", "DZ", "AD", "AO", "AG", "AR", 
    "AM", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", 
    "BJ", "BT"), Name = c("Afghanistan", "Albania", "Algeria", "Andorra", 
    "Angola", "Antigua and Barbuda", "Argentina", "Armenia", "Australia", 
    "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", 
    "Barbados", "Belarus", "Belgium", "Belize", "Benin", "Bhutan"
    ), readi_readiness = c(0.246442145333371, 0.410559121234659, 
    0.33334509314068, 0.478714965470705, 0.268117798333208, 0.449260130429268, 
    0.376037316185072, 0.50573276439272, 0.690552124980166, 0.68230738466746, 
    0.447625682772037, 0.427831425066084, 0.519334145918882, 0.281041117917878, 
    0.53612684461446, 0.490112049917615, 0.600207432355383, 0.333388191619152, 
    0.337878182675485, 0.483057133272884)), class = "data.frame", row.names = c(NA, 
    -20L))
    
    
    # Define UI for the map and table
    ui <- fluidPage(
      tags$head(
        tags$style(HTML("
          .dataTables_wrapper .dataTables_filter {
            float: left !important;
            text-align: left !important;
          }
          .dataTables_wrapper .dataTables_filter input {
            margin-left: 0.5em;
            width: 200px;
          }
          .table-container {
            display: flex;
            flex-direction: row;
          }
          .table-container .table-controls {
            margin-right: 2em;
          }
          @media (max-width: 768px) {
            .table-container {
              flex-direction: column;
            }
            .table-container .table-controls {
              margin-right: 0;
              margin-bottom: 1em;
            }
            .shiny-input-container {
              width: 100%;
            }
            .dataTables_wrapper .dataTables_filter input {
              width: 100%;
            }
            .main-panel, .content {
              height: calc(100vh - 56px) !important;
              overflow: auto;
            }
            .main-panel {
              height: 100vh;
            }
          }
        "))
      ),
      titlePanel(div(span("Country Rankings"), style={'padding-top: 15px'})),
      tabsetPanel(
        tabPanel("Vulnerability Map",
          sidebarLayout(
            sidebarPanel(
              selectInput("year", "Select Year", choices = c("", 2021), selected = NULL),
              selectInput("category", "Select Category", 
                          choices = list("Adaptive Capacity" = "vul_capacity"), selected = "vul_vulnerability"),
              textOutput("category_context")
            ),
            mainPanel(
              htmlOutput("map")
            )
          )
        ),
        tabPanel("Vulnerability Table",
          sidebarLayout(
            sidebarPanel(
              selectInput("table_year", "Select Year", choices = 2021, selected = 2021),
              selectInput("table_category", "Select Category", 
                          choices = list("Adaptive Capacity" = "vul_capacity"), selected = "vul_capacity")
            ),
            mainPanel(
              DTOutput("vulnerability_table")
            )
          )
        ),
        tabPanel("Readiness Map",
          sidebarLayout(
            sidebarPanel(
              selectInput("readiness_year", "Select Year", choices = 2021, selected = 2021),
              selectInput("readiness_category", "Select Category", 
                          choices = list("Overall Readiness" = "readi_readiness"), selected = "readi_readiness"),
              textOutput("readiness_category_context")
            ),
            mainPanel(
              htmlOutput("readiness_map")
            )
          )
        ),
        tabPanel("Readiness Table",
          sidebarLayout(
            sidebarPanel(
              selectInput("readiness_table_year", "Select Year", choices = 1995:2021, selected = 2021),
              selectInput("readiness_table_category", "Select Category", 
                          choices = list("Economic" = "readi_economic", 
                                         "Governance" = "readi_governance", "Overall Readiness" = "readi_readiness",
                                         "Social" = "readi_social"), selected = "readi_readiness")
            ),
            mainPanel(
              DTOutput("readiness_table")
            )
          )
        )
      )
    )
    
    # Define server logic for the map and table
    server <- function(input, output, session) {
      output$category_context <- renderText({
        context <- switch(input$category,
                          "vul_capacity" = "The availability of social resources for sector-specific adaptation. In some cases, these capacities reflect sustainable adaptation solutions. In other cases, they reflect capacities to put newer, more sustainable adaptations into place.", "")
        context
      })
      
      output$map <- renderGvis({
        if (input$year == "") return ()
        year <- input$year
        category <- input$category
        
        # Create the column name for the selected year and category
        rank_column <- paste(category, "_Rank_", year, sep = "")
        
        # Filter and prepare the data
        map_data <- combined_vul_data %>%
          select(ISO3, Name, Rank = !!sym(rank_column)) %>%
          mutate(Tooltip = ifelse(is.na(Rank), paste(Name, "- Insufficient data"), paste(Name)))
        
        
        # Update locationvar column
        map_data <- map_data %>%
          mutate(Location = ifelse(Name %in% c("Congo (Brazzaville)", "Congo (Kinshasa)"), ISO3, Name))
        
        # Determine the range of the Rank values
        min_rank <- min(map_data$Rank, na.rm = TRUE)
        max_rank <- max(map_data$Rank, na.rm = TRUE)
        
        # Create the map
        gvisGeoChart(map_data, locationvar = "Location", colorvar = "Rank",
                     hovervar = "Tooltip",  
                     options = list(colorAxis = paste0("{minValue:", min_rank, ", maxValue:", max_rank, ", colors:['#00FF00', '#FFFF00', '#FF0000']}"),
                                    backgroundColor = '#81d4fa', datalessRegionColor = '#f5f5f5',
                                    defaultColor = '#f5f5f5',
                                    tooltip = "{isHtml: true}",
                                    width = "100%",
                                    height = 500))  
      })
    
      output$vulnerability_table <- renderDT({
        year <- input$table_year
        category <- input$table_category
    
        # Prepare data for the table
        table_data <- prepare_table_data(combined_vul_data, year, category)
    
        # Calculate min and max rank
        min_rank <- min(as.numeric(table_data$Rank), na.rm = TRUE)
        max_rank <- max(as.numeric(table_data$Rank), na.rm = TRUE)
    
        # Generate a color scale
        color_scale <- colorRampPalette(c('#00FF00', '#FFFF00', '#FF0000'))(max_rank - min_rank + 1)
    
        # Render the interactive table
        datatable(table_data, options = list(
          dom = 'Bt',
          pageLength = -1, 
          autoWidth = TRUE, 
          scrollY = FALSE,
          columnDefs = list(
            list(targets = 0, className = 'dt-right')
          )
        ), 
        rownames = FALSE, 
        filter = list(position = 'top', clear = FALSE, plainText = TRUE), 
        escape = FALSE) %>%
          formatStyle('Rank', 
                      backgroundColor = styleEqual(seq(min_rank, max_rank), color_scale),
                      color = 'black')
      })
    
      output$readiness_category_context <- renderText({
        context <- switch(input$readiness_category,
                          "readi_readiness" = "Overall readiness measures a country's ability to leverage investments and convert them to adaptation actions. It combines economic, governance, and social readiness scores to provide a comprehensive measure.",
                          "")
        context
      })
      
      output$readiness_map <- renderGvis({
        year <- input$readiness_year
        category <- input$readiness_category
        
        # Create the column name for the selected year and category
        rank_column <- paste(category, "_Rank_", year, sep = "")
        
        # Filter and prepare the data
        map_data <- combined_readi_data %>%
          select(ISO3, Name, Rank = !!sym(rank_column)) %>%
          mutate(Location = ifelse(Name %in% c("Congo (Brazzaville)", "Congo (Kinshasa)"), ISO3, Name)) %>%
          mutate(Tooltip = ifelse(is.na(Rank), paste(Name, "- Insufficient data"), paste(Name)))
        
        # Determine the range of the Rank values
        min_rank <- min(map_data$Rank, na.rm = TRUE)
        max_rank <- max(map_data$Rank, na.rm = TRUE)
        
        # Create the map
        gvisGeoChart(map_data, locationvar = "Location", colorvar = "Rank",
                     hovervar = "Tooltip",  # Use the Tooltip column
                     options = list(colorAxis = paste0("{minValue:", min_rank, ", maxValue:", max_rank, ", colors:['#00FF00', '#FFFF00', '#FF0000']}"),
                                    backgroundColor = '#81d4fa', datalessRegionColor = '#f5f5f5',
                                    defaultColor = '#f5f5f5',
                                    tooltip = "{isHtml: true}",
                                    width = "500px",
                                    height = "500px"))  
      })
    
      output$readiness_table <- renderDT({
        year <- input$readiness_table_year
        category <- input$readiness_table_category
    
        # Prepare data for the table
        table_data <- prepare_table_data_readiness(combined_readi_data, year, category)
    
        # Calculate min and max rank
        min_rank <- min(as.numeric(table_data$Rank), na.rm = TRUE)
        max_rank <- max(as.numeric(table_data$Rank), na.rm = TRUE)
    
        # Generate a color scale
        color_scale <- colorRampPalette(c('#00FF00', '#FFFF00', '#FF0000'))(max_rank - min_rank + 1)
    
        # Render the interactive table
        datatable(table_data, options = list(
          dom = 'Bt', 
          buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
          pageLength = -1, 
          autoWidth = TRUE, 
          scrollY = FALSE,
          columnDefs = list(
            list(targets = 0, className = 'dt-right')
          )
        ), 
        rownames = FALSE, 
        filter = list(position = 'top', clear = FALSE, plainText = TRUE), 
        escape = FALSE) %>%
          formatStyle('Rank', 
                      backgroundColor = styleEqual(seq(min_rank, max_rank), color_scale),
                      color = 'black')
      })
    }
    
    # Prepare data for the table
    prepare_table_data <- function(data, year, category) {
      rank_column <- paste(category, "_Rank_", year, sep = "")
      
      data <- data %>%
        select(ISO3, Name, !!rank_column) %>%
        rename(Rank = !!rank_column) %>%
        mutate(Rank = ifelse(is.na(Rank), "Insufficient data", Rank)) %>%
        select(Rank, Country = Name) %>%
        arrange(as.numeric(Rank))
      
      return(data)
    }
    
    # Prepare data for the readiness table
    prepare_table_data_readiness <- function(data, year, category) {
      rank_column <- paste(category, "_Rank_", year, sep = "")
      
      data <- data %>%
        select(ISO3, Name, !!rank_column) %>%
        rename(Rank = !!rank_column) %>%
        mutate(Rank = ifelse(is.na(Rank), "Insufficient data", Rank)) %>%
        select(Rank, Country = Name) %>%
        arrange(as.numeric(Rank))
      
      return(data)
    }
    
    shinyApp(ui = ui, server = server)
    ```