Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to arrange Shiny app cards in a masonry grid?

I am currently trying to create a "wall" of cards from a dataframe for a hobby Shiny app. Adapting from this SO answer, here is a minimal complete example of my code:

library(shiny)

df <- read.csv("https://github.com/intelligence-refinery/shiny_app/raw/master/flyer.csv")

card <- function(img, item, store) {
    HTML(
        paste0(
            '<div class="card">
              <div class="container">
              <h4><b>', item, '</b></h4>
              </div>
              <img src="', img, '" style="width:100%">
              <div class="container">
              <h5><i>', store, '</i></h5>
              </div>
              </div>'
        )
    )
}


ui <- fluidPage(
    tags$head(tags$style('.card {
                         width: 250px;
                       clear: both;
                       /* Add shadows to create the "card" effect */
                       box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
                       transition: 0.3s;
                       }
                       /* On mouse-over, add a deeper shadow */
                       .card:hover {
                       box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
                       }
                       /* Add some padding inside the card container */
                       .container {
                       width: 250px;
                       padding: 2px 16px;
                       }')),
    uiOutput("cards")
)

server <- function(input, output, session) {
    output$cards <- renderUI({
        # First make the cards
        args <-
            lapply(1:dim(df)[1], function(x)
                card(df[x, "clean_image_url"],
                     store = df[x, "store_name"],
                     item = df[x, "name"]))

        # Make sure to add other arguments to the list:
        args$cellArgs <- list(style = "
            width: 300px;
            height: auto;
            margin: 5px;
            ")

        # basically the same as flowLayout(cards[[1]], cards[[2]],...)
        do.call(shiny::flowLayout, args)

    })
}


# Preview the UI in the console
shinyApp(ui = ui, server = server)


However, this results in unsightly gaps between the cards, as they are different size:

gaps between cards

A masonry grid layout would be ideal in this situation:

masonry grid example

Since I'm very new to web work of any kind, I'm unsure as to how to apply the pure CSS/Javascript solutions to my Shiny app. I have tried to adapt the example from the Bootstrap docs, but when I pasted the HTML and CSS to CodePen, I am not getting a complete example...

The closest that I have found to an example of creating a masonry grid in a Shiny app is the (quite complex) conference Twitter dashboard. I found the relevant section in the code. However, as I am quite new to any web work, I can't adapt it to my own code.

Is there a way to create a masonry layout for a panel of cards generated this way? Any help would be greatly appreciated! :)

like image 799
DMNO Avatar asked Sep 16 '19 19:09

DMNO


People also ask

Which function's is are used to customize the layout of R shiny application?

To create a layout based on the fluid system you use the fluidPage() function. To create rows within the grid you use the fluidRow() function; to create columns within rows you use the column() function.

Which arguments sidebarLayout takes while creating a shiny app?

sidebarLayout always takes two arguments: sidebarPanel function output. mainPanel function output.

Does bootstrap Use shiny?

You've also learned a little bit about what's going on under the hood: you know that Shiny uses Bootstrap, and that the input and output functions just return HTML, which you can also create yourself.

Which function is used to add a shiny server component?

shinyApp. Finally, we use the shinyApp function to create a Shiny app object from the UI/server pair that we defined above. We save all of this code, the ui object, the server function, and the call to the shinyApp function, in an R script called app.


1 Answers

If you can divide your images into some meaninful number of columns, then before laying out your horizontal layout, you can wrap sets of images in column. Then arrange the columns with fluidRow.

Here is an example using just the first 20 rows from your data. Pasting the server only. The other change was to add df <- df[1:20,] after loading the data:

 server <- function(input, output, session) {
    output$cards <- renderUI({
        # First make the cards
        args <-
            lapply(1:dim(df)[1], function(x)
                card(df[x, "clean_image_url"],
                     store = df[x, "store_name"],
                     item = df[x, "name"]))

        # Make sure to add other arguments to the list:
        args$cellArgs <- list(style = "
            width: 300px;
            height: auto;
            margin: 5px;
            ")

        # make five columns with four images each
        cols <-
          lapply(seq(4, dim(df)[1], 4), function(x) {
              column(width = 2, verticalLayout(args[(x - 3):x], fluid = TRUE))
          })


        # then use fluidRow to arrange the columns 
        do.call(shiny::fluidRow, cols)

    })
}

You should get something like this:

enter image description here

like image 179
teofil Avatar answered Sep 24 '22 06:09

teofil