Search code examples
htmlcssrshinyshinyjs

How to adjust the positioning of objects rendered using shinyJS?


In the code excerpt posted at the bottom, a well panel with embedded objects is rendered underneath the action buttons, when the "Delete column" action button is clicked. The well panel disappears when the "Add column" action button is clicked. This works as intended.

However, I'm having a difficult time fine-tuning the width of the well panel and objects rendered inside the well panel as illustrated below. I've been fiddling with column(width...), and style = "margin-xxx;" or "padding: xxx;" and I can't quite get these "tricks" to work right. Is there a better way to make these sorts of positioning adjustments?

If the code looks long, it's only because I show all the different types of adjustments I've been throwing at it. It doesn't do much.

enter image description here

Code:

library(shiny)
library(shinyjs)

toggleView <- function(input, output_name){
  observeEvent(input$delSeries, {show(output_name)})
  observeEvent(input$addSeries, {hide(output_name)})
}

ui <- fluidPage(
  useShinyjs(),
  # CSS for formatting action buttons:
  tags$head(tags$style(HTML(      
        "#seriesInst, #addSeries, #delSeries, #savTbl, #clrTbl {
            color: white;
            background-color: silver;
            box-shadow: 2px 2px 2px 2px grey;
            line-height:1;
            font-weight:500;
      }"))),
  br(),
  fluidRow(
      column(2,actionButton("addSeries","Add column")),
      column(2,actionButton("delSeries","Delete column")),
      column(2,actionButton("savTbl","Save table")),
      column(2,actionButton("clrTbl","Clear table")),
      br(),
      ),
  br(),
  fluidRow(
      column(9,
          fluidRow(id = "my_well", 
            column(5,
               h5((hidden((textOutput("delFlag"))))),
               class = c("nudge-left", "text-left"),
               style = "margin-left:10px;"
            ),
            column(4,
               hidden(uiOutput("delSeries2")),
               class = "nudge-right",
               style = "margin-top: 2px;"
            ),
            # adjust well panel position and dimensions:
            style="height:40px;        
                   margin-top: -2px;
                   margin-bottom:0px;
                   margin-left:0px;
                   margin-right:0px;
                   padding: 0px;"
      )))
  )

server <- function(input,output,session)({
  
  output$delFlag <- renderText("Delete series:")
  
  output$delSeries2 <- 
    renderUI(
      selectInput("delSeries3", 
                  label = NULL,
                  choices = c(1,2,3), 
                  selected = "",
                  multiple = TRUE,
                  width = '110px')
    )
  
  toggleView(input,"delSeries2")
  toggleView(input,"delFlag")
  
  # below renders well panel around the delete series objects:
  observeEvent(input$delSeries, addClass("my_well", "well"), ignoreInit = TRUE)
  observeEvent(input$addSeries, removeClass("my_well", "well"), ignoreInit = TRUE)

}) 

shinyApp(ui, server)

Solution

  • See below, where this can be accomplished with CSS using tags$div() and tag$style() functions:

    library(shiny)
    library(shinyjs)
    
    toggleView <- function(input, output_name){
      observeEvent(input$delSeries, {show(output_name)})
      observeEvent(input$addSeries, {hide(output_name)})
    }
    
    ui <- fluidPage(
      useShinyjs(),
      # CSS for formatting action buttons:
      tags$head(tags$style(HTML(      
            "#seriesInst, #addSeries, #delSeries, #savTbl, #clrTbl {
                color: white;
                background-color: silver;
                box-shadow: 2px 2px 2px 2px grey;
                line-height:1;
                font-weight:500;
          }"))),
      br(),
      fluidRow(
          column(2,actionButton("addSeries","Add column")),
          column(2,actionButton("delSeries","Delete column")),
          column(2,actionButton("savTbl","Save table")),
          column(2,actionButton("clrTbl","Clear table")),
          br(),
          ),
      br(),
      fluidRow(
          column(12,
              fluidRow(
                tags$div(id="my_well", # tags$div(...) added to adjust well panel width 
                    column(5,
                      tags$div(id="del_Flag", # tags$div(...) added to adjust text position
                         h5((hidden((textOutput("delFlag"))))),
                         tags$style(type="text/css","#del_Flag{text-align:right;width:120px;}") # adjust text position
                      )
                    ),
                    column(7,
                      tags$div(id="del_Series2", # tags$div(...) added to adjust selectInput position     
                         hidden(uiOutput("delSeries2")),
                         style = "margin-top: 2px;",
                         tags$style(type="text/css","#del_Series2{margin:auto;width:100px;}") # adjust selectInput position
                      )
                    ),
                    style="height:40px;        
                           margin-top: -2px;
                           margin-bottom:0px;
                           margin-left:0px;
                           margin-right:0px;
                           padding: 0px;",
                    tags$style(type="text/css","#my_well{font-size:14px;width:565px;}") # adjust well panel width
                )
              )
          )
        )
      )
    
    server <- function(input,output,session)({
      
      output$delFlag <- renderText("Delete series:")
      
      output$delSeries2 <- 
        renderUI(
          selectInput("delSeries3", 
                      label = NULL,
                      choices = c(1,2,3), 
                      selected = "",
                      multiple = TRUE,
                      width = '110px')
        )
      
      toggleView(input,"delSeries2")
      toggleView(input,"delFlag")
      
      # below renders well panel around the delete series objects:
      observeEvent(input$delSeries, addClass("my_well", "well"), ignoreInit = TRUE)
      observeEvent(input$addSeries, removeClass("my_well", "well"), ignoreInit = TRUE)
    
    }) 
    
    shinyApp(ui, server)