Search code examples
cssrslickr

Moving arrow buttons to dots in slickR


Very similar to this SO question, I would like to move the previous/next arrow buttons. I want to move them so that they inline with dots, and the result should look like this:

enter image description here

Default placement for the arrows in slickR are to the sides of the carousel (code to produce is below): enter image description here

According to the linked SO question, in settings function there is an argument called appendArrows which allows us to specify which div should the arrows appear in.

If I inspect the following shiny app, the slickR dots appear in a class called slick-dots. When I add appendArrows = ".slick-dots" the existing arrows completely disappear.

Info related to the original, JavaScript-based slick, (of which 'slickR' is based on, seem to suggest creating a new class and then assigning appendRows to the new class. However I am trying to add to existing dots class.

My JavaScript/HTML is very limited but I found some helpful resources:

Shiny app code:

library(shiny)
library(slickR)

ui <- fluidPage(
  slickROutput("slickr_test", width = "95%")
)

server <- function(input, output) {
  
  output$slickr_test <- renderSlickR({
    
    slickR(
      obj = c(
        "https://upload.wikimedia.org/wikipedia/commons/9/9e/Flag_of_Japan.svg",
        "https://upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_South_Korea.svg",
        "https://upload.wikimedia.org/wikipedia/commons/6/69/Flag_of_Monaco_%28state%29.svg"
      )
    ) +
      settings(
        dots = TRUE
        # ,appendArrows = ".slick-dots" 
      )
    
  })
  
}

shinyApp(ui = ui, server = server)

UPD following @Tim's answer.

Using Tim's normal arrow code, and with the inclusion of a DT Datatable, there seems to be multiple arrows introduced:

library(shiny)
library(slickR)

ui <- fluidPage(
  tags$head(
    tags$style(HTML("
      .slick-pager {
        display: flex;
        justify-content: center;
        align-items: center;
        margin-top: 10px;
      }
      .slick-prev, .slick-next {
        position: relative;
        display: inline-block;
        margin: 0px 30px; /* adjust distance between buttons and dots in second margin*/
      }
      
      .slick-dots {
        bottom: 9px; /* slight height adjust to make dots align with buttons*/
      }
      ul.slick-dots { /* this is the div that wraps the dots, we need to paint it black */
          background: black;
          width: 160px;
      }
      .slick-dots li button:before {
          color: white;  /* Inactive dots */
          opacity: 1;
          font-size: 12px; /* change dot size! */
      }
      
      .slick-dots li.slick-active button:before {
          color: orange; /* Active dot */
          opacity: 1;
          font-size: 12px; /* change dot size! */
      }
      
      /* Style the previous and next buttons */
      .slick-prev, .slick-next {
          background-color: black;  /* Black background */
          color: white;             /* White arrow */
      }
      .slick-next:before, .slick-prev:before{
          color:white !important; 
          opacity: 1;
      }
      
    "))
  ),
  slickROutput("slickr_test", width = "95%"),
  tags$script(HTML("
    $(document).on('shiny:value', function(event) {
      setTimeout(function() {
        $('.slick-dots').wrap('<div class=\"slick-pager\"></div>');
        $('.slick-prev, .slick-next').appendTo('.slick-pager');
      }, 10);
    });
  ")),
  DTOutput("iris_dt")
  
)

server <- function(input, output) {
  
  output$slickr_test <- renderSlickR({
    slickR(
      obj = c(
        "https://upload.wikimedia.org/wikipedia/commons/9/9e/Flag_of_Japan.svg",
        "https://upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_South_Korea.svg",
        "https://upload.wikimedia.org/wikipedia/commons/6/69/Flag_of_Monaco_%28state%29.svg"
      )
    ) +
      settings(
        dots = TRUE
      )
  })
  
  output$iris_dt <- renderDT({iris})
  
}

shinyApp(ui = ui, server = server)

enter image description here

enter image description here

It seems like when there is a DTOutput, the slick-pager HTML code gets nested inside another slick-pager. And while the arrows are floating over DT for this example - preventing the arrows from being clicked, in my own they are between images and DT. In my own code (since I can click on the arrows), there are even more arrows as the carousel is swiped forward.

In my own code, the arrows look like this before carousel is swiped: enter image description here

And arrows look like this when swiped once: enter image description here


Solution

  • This comes fairly close to your requirement.

    1. After loading place before- & next- buttons inside the dots-div.
    2. Add loads of css to make the dots-div have a darker style with bigger dots and yellow active-dot.

    out

    Code for normal arrows with DTOutput below

    If you add a DTOutput below the carousel, the dots will overlap the table by default. I added some margins to create some space between the dots and the table. Also Im now placing the prev- & next buttons inside the .slick-dots div.

    library(shiny)
    library(slickR)
    library(DT)
    
    ui <- fluidPage(
      tags$head(
        tags$style(HTML("
          
          .slick-prev, .slick-next {
            margin: 0px 30px; /* adjust distance between buttons and dots in second margin*/
            /* position: relative; */
            display: inline-block;
            margin: 0px 30px;
          }
          
          .slick-dots {
            position: relative;
            bottom: 30px;
          }
          ul.slick-dots { /* this is the div that wraps the dots, we need to paint it black */
              background: black;
              width: 160px;
          }
          .slick-dots li button:before {
              color: white;  /* Inactive dots */
              opacity: 1;
              font-size: 12px; /* change dot size! */
          }
          
          .slick-dots li.slick-active button:before {
              color: orange; /* Active dot */
              opacity: 1;
              font-size: 12px; /* change dot size! */
          }
          
          /* Style the previous and next buttons */
          .slick-prev, .slick-next {
              background-color: black;  /* Black background */
              color: white;             /* White arrow */
          }
          .slick-next:before, .slick-prev:before{
              color:white !important; 
              opacity: 1;
          }
          
          .dataTables_wrapper {
              position: relative;
              clear: both;
              margin-top: 250px;
          }
          
        "))
      ),
      slickROutput("slickr_test", width = "95%"),
      tags$script(HTML("
        $(document).on('shiny:value', function(event) {
          setTimeout(function() {
            $('.slick-prev, .slick-next').appendTo('.slick-dots');
          }, 10);
        });
      ")),
      DTOutput("iris_dt")
      
    )
    
    server <- function(input, output) {
      
      output$slickr_test <- renderSlickR({
        slickR(
          obj = c(
            "https://upload.wikimedia.org/wikipedia/commons/9/9e/Flag_of_Japan.svg",
            "https://upload.wikimedia.org/wikipedia/commons/0/09/Flag_of_South_Korea.svg",
            "https://upload.wikimedia.org/wikipedia/commons/6/69/Flag_of_Monaco_%28state%29.svg"
          )
        ) +
          settings(
            dots = TRUE
          )
      })
      
      output$iris_dt <- renderDT({iris})
      
    }
    
    shinyApp(ui = ui, server = server)
    

    How to adjust it

    1. Open the Shiny App in browser open
    2. Press F12 to open the Developer console.
    3. CTRL+Shift+C and then hover your mouse over div elements you want to inspect.
    4. look at the css and make changes in realtime until it fits. Then copy the css into your app :)
    5. For example here I am adjusting the background of the dots-div back