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:
A masonry grid layout would be ideal in this situation:
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! :)
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.
sidebarLayout always takes two arguments: sidebarPanel function output. mainPanel function output.
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.
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With