Below is a step by step
introduction to the {bs4Dash}
structure.
This is the template to start with {bs4Dash}
:
library(shiny)
library(bs4Dash)
shinyApp(
ui = dashboardPage(
title = "Basic Dashboard",
header = dashboardHeader(),
sidebar = dashboardSidebar(),
controlbar = dashboardControlbar(),
footer = dashboardFooter(),
body = dashboardBody()
),
server = function(input, output) {}
)
The dashboardPage()
is the main wrapper:
dashboardPage(
header,
sidebar,
body,
controlbar = NULL,
footer = NULL,
title = NULL,
freshTheme = NULL,
preloader = NULL,
options = NULL,
fullscreen = FALSE,
help = FALSE,
dark = FALSE,
scrollToTop = FALSE
)
has mandatory slots for the navbar (dashboardHeader()
),
sidebar (dashboardSidebar()
) and
(dashboardBody()
). Note the
dashboardControlbar()
and dashboardFooter()
are optional. The title parameter gives its name to the
web browser tab. freshTheme, when provided, expects a
{fresh}
powered theme
created with fresh::create_theme()
. It allows deeper
customization of colors to fit very specific needs like industry brand
colors. preloader expects a loader tag built with
waiter
, see more here, for instance:
At the moment, options are not available, but the idea is to provide deeper customization of the AdminLTE3 template like changing the sidebars and cards animation speed, …
When fullscreen is TRUE, an icon is displayed in the
navbar to switch to full screen mode. help
automatically enable/disable all tooltips and popover that are present
in the shiny app: this is an easier approach than using the server
methods addPopover()
, addTooltip()
, … but less
specific. dark allows to toggle the dark mode: if
FALSE, the theme switch is hidden and the dashboard takes the light
design. scrollToTop allows to toggle the scroll to top
button shown in the bottom right corner.
Now, it is time to fill this template!
dashboardBody()
is the main dashboard container:
dashboardBody(
tabItems(
tabItem(
tabName = "item1",
fluidRow(
lapply(1:3, FUN = function(i) {
sortable(
width = 4,
p(class = "text-center", paste("Column", i)),
lapply(1:2, FUN = function(j) {
box(
title = paste0("I am the ", j, "-th card of the ", i, "-th column"),
width = 12,
"Click on my header"
)
})
)
})
)
),
tabItem(
tabName = "item2",
box(
title = "Card with messages",
width = 9,
userMessages(
width = 12,
status = "success",
userMessage(
author = "Alexander Pierce",
date = "20 Jan 2:00 pm",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user1-128x128.jpg",
type = "received",
"Is this template really for free? That's unbelievable!"
),
userMessage(
author = "Dana Pierce",
date = "21 Jan 4:00 pm",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user5-128x128.jpg",
type = "sent",
"Indeed, that's unbelievable!"
)
)
)
)
)
)
The principle is pretty straightforward: all
dashboardBody()
elements must be embeded in a
tabItems()
list containing as may elements as the number of
items. Each item is a tabItem()
. Importantly, the
tabName argument must be provide and unique. Moreover,
it must be identical to the corresponding menuItem()
, so
that the navigation between tabs work. This is exactly the same
principle as for {shinydashboard}
. Therefore, users should
not be lost.
In practice, if the sidebar is empty (without menu), it is still
possible to get rid of tabItems()
and
tabItem()
.
Below is the code for your first {bs4Dash}
application:
shinyApp(
ui = dashboardPage(
title = "Basic Dashboard",
fullscreen = TRUE,
header = dashboardHeader(
title = dashboardBrand(
title = "bs4Dash",
color = "primary",
href = "https://www.google.fr",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user2-160x160.jpg",
),
skin = "light",
status = "white",
border = TRUE,
sidebarIcon = icon("bars"),
controlbarIcon = icon("th"),
fixed = FALSE,
leftUi = tagList(
dropdownMenu(
badgeStatus = "info",
type = "notifications",
notificationItem(
inputId = "triggerAction2",
text = "Error!",
status = "danger"
)
),
dropdownMenu(
badgeStatus = "info",
type = "tasks",
taskItem(
inputId = "triggerAction3",
text = "My progress",
color = "orange",
value = 10
)
)
),
rightUi = dropdownMenu(
badgeStatus = "danger",
type = "messages",
messageItem(
inputId = "triggerAction1",
message = "message 1",
from = "Divad Nojnarg",
image = "https://adminlte.io/themes/v3/dist/img/user3-128x128.jpg",
time = "today",
color = "lime"
)
)
),
sidebar = dashboardSidebar(
skin = "light",
status = "primary",
elevation = 3,
sidebarUserPanel(
image = "https://image.flaticon.com/icons/svg/1149/1149168.svg",
name = "Welcome Onboard!"
),
sidebarMenu(
sidebarHeader("Header 1"),
menuItem(
"Item 1",
tabName = "item1",
icon = icon("sliders")
),
menuItem(
"Item 2",
tabName = "item2",
icon = icon("id-card")
)
)
),
controlbar = dashboardControlbar(
skin = "light",
pinned = TRUE,
collapsed = FALSE,
overlay = FALSE,
controlbarMenu(
id = "controlbarmenu",
controlbarItem(
title = "Item 1",
sliderInput(
inputId = "obs",
label = "Number of observations:",
min = 0,
max = 1000,
value = 500
),
column(
width = 12,
align = "center",
radioButtons(
inputId = "dist",
label = "Distribution type:",
c(
"Normal" = "norm",
"Uniform" = "unif",
"Log-normal" = "lnorm",
"Exponential" = "exp"
)
)
)
),
controlbarItem(
"Item 2",
"Simple text"
)
)
),
footer = dashboardFooter(
left = a(
href = "https://twitter.com/divadnojnarg",
target = "_blank", "@DivadNojnarg"
),
right = "2018"
),
body = dashboardBody(
tabItems(
tabItem(
tabName = "item1",
fluidRow(
lapply(1:3, FUN = function(i) {
sortable(
width = 4,
p(class = "text-center", paste("Column", i)),
lapply(1:2, FUN = function(j) {
box(
title = paste0("I am the ", j, "-th card of the ", i, "-th column"),
width = 12,
"Click on my header"
)
})
)
})
)
),
tabItem(
tabName = "item2",
box(
title = "Card with messages",
width = 9,
userMessages(
width = 12,
status = "success",
userMessage(
author = "Alexander Pierce",
date = "20 Jan 2:00 pm",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user1-128x128.jpg",
type = "received",
"Is this template really for free? That's unbelievable!"
),
userMessage(
author = "Dana Pierce",
date = "21 Jan 4:00 pm",
image = "https://adminlte.io/themes/AdminLTE/dist/img/user5-128x128.jpg",
type = "sent",
"Indeed, that's unbelievable!"
)
)
)
)
)
)
),
server = function(input, output) {}
)
* All credits go to https://codyhouse.co/gem/css-jquery-image-comparison-slider/ for the nice image slider widget!
Advanced shiny user would probably design shiny modules to generate this page, which I really encourage. However, how to deal with modules is not the purpose of this article.