I would like to have both a persistent sidebar (as in shinydashboard layout) and a navigation bar with tabs (as in shiny::navbarPage layout). I came across this answer that seems to correspond to what I want.
The problem is that inputs in the sidebar are not persistent through tabs, i.e when switching tabs, the inputs in the sidebar are not displayed anymore (at the contrary of shinydashboard sidebar for example). Here's an example I cannot really minimize more since a lot of it is CSS:
library(shiny)
library(bootstraplib)
# boot dash layout funs ---------------------------------------------------
boot_side_layout <- function(...) {
div(class = "d-flex wrapper", ...)
}
boot_sidebar <- function(...) {
div(
class = "bg-light border-right sidebar-wrapper",
div(class = "list-group list-group-flush", ...)
)
}
boot_main <- function(...) {
div(
class = "page-content-wrapper",
div(class = "container-fluid", ...)
)
}
# css ---------------------------------------------------------------------
css_def <- "
body {
overflow-x: hidden;
}
.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
padding-left: 0px;
}
.sidebar-wrapper {
min-height: 100vh;
margin-left: -15rem;
padding-left: 15px;
padding-right: 15px;
-webkit-transition: margin .25s ease-out;
-moz-transition: margin .25s ease-out;
-o-transition: margin .25s ease-out;
transition: margin .25s ease-out;
}
.sidebar-wrapper .list-group {
width: 15rem;
}
.page-content-wrapper {
min-width: 100vw;
padding: 20px;
}
.wrapper.toggled .sidebar-wrapper {
margin-left: 0;
}
.sidebar-wrapper, .page-content-wrapper {
padding-top: 20px;
}
.navbar{
margin-bottom: 0px;
}
.navbar-collapse {
font-size: 1.1rem
}
#media (max-width: 768px) {
.sidebar-wrapper {
padding-right: 0px;
padding-left: 0px;
}
}
#media (min-width: 768px) {
.sidebar-wrapper {
margin-left: 0;
position: fixed;
}
.page-content-wrapper {
min-width: 0;
width: 100%;
}
.wrapper.toggled .sidebar-wrapper {
margin-left: -15rem;
}
}
"
# app ---------------------------------------------------------------------
ui <- tagList(
tags$head(tags$style(HTML(css_def))),
bootstrap(),
navbarPage(
collapsible = TRUE,
title = "",
tabPanel(
"Statistics",
boot_side_layout(
boot_sidebar(
selectInput(
"variables",
"Variables",
NULL
)
),
boot_main(
fluidRow(
dataTableOutput("statistics")
)
)
)
),
tabPanel(
"Plots",
boot_side_layout(
boot_sidebar(
),
boot_main(
)
)
)
)
)
server <- function(input, output, session) {
output$statistics <- renderDataTable(mtcars[10, 10])
}
shinyApp(ui, server)
How can I make these inputs persistent through sidebar? (If somebody knows of another simple way to mix persistent sidebar with navbar, please show it as well).
Why not using a sidebarLayout with a navbarPage in mainPanel?
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("select", "Select", c("a", "b", "c"))
),
mainPanel(
navbarPage(
"App Title",
tabPanel("Plot"),
tabPanel("Summary"),
tabPanel("Table")
)
)
)
)
shinyApp(ui, server)
EDIT
Or something like this?
library(shiny)
library(ggplot2)
ui <- fluidPage(
div(
style = "display: flex; flex-direction: column;",
div( #~~ Main panel ~~#
navbarPage(
"Old Faithful Geyser Data",
tabPanel(
"Plot",
plotOutput("ggplot")
),
tabPanel("Summary"),
tabPanel("Table")
)
),
wellPanel( #~~ Sidebar ~~#
style = "width: 300px;",
sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30),
)
)
)
server <- function(input, output) {
output[["ggplot"]] <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
shinyApp(ui = ui, server = server)
EDIT
Like this to have the sidebar on the left:
library(shiny)
library(shinyjs)
library(ggplot2)
CSS <- "
.sidebar {
min-width: 300px;
margin-right: 30px;
}
#sidebar {
width: 300px;
}
"
ui <- fluidPage(
useShinyjs(),
tags$head(tags$style(HTML(CSS))),
div( #~~ Main panel ~~#
navbarPage(
"Old Faithful Geyser Data",
tabPanel(
"Plot",
div(
style = "display: flex;",
div(class = "sidebar"),
plotOutput("ggplot")
)
),
tabPanel(
"Summary",
div(
style = "display: flex;",
div(class = "sidebar"),
verbatimTextOutput("summary")
)
),
tabPanel(
"Table",
div(
style = "display: flex;",
div(class = "sidebar"),
tableOutput("table")
)
),
id = "navbar"
)
),
wellPanel( #~~ Sidebar ~~#
id = "sidebar",
sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30),
)
)
server <- function(input, output) {
output[["ggplot"]] <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
output[["summary"]] <- renderPrint({
list(a = 1:10, b = 1:10)
})
output[["table"]] <- renderTable({
iris[1:10,]
})
observeEvent(input[["navbar"]], {
selector <-
sprintf("$('div.tab-pane[data-value=\"%s\"] div.sidebar')", input[["navbar"]])
runjs(paste0(selector, ".append($('#sidebar'));"))
})
}
shinyApp(ui = ui, server = server)
EDIT
Here is an improvement of the above way. I've made some convenient functions tabPanel2 and sidebar to help the user. And I use fluidRow and column instead of using a display: flex;. This allows to have a sidebar width relative to the screen size. The example below also shows how to not include the sidebar in a tab (simply use tabPanel and not tabPanel2.
library(shiny)
library(shinyjs)
library(ggplot2)
tabPanel2 <- function(title, ..., value = title, icon = NULL, sidebarWidth = 4){
tabPanel(
title = title,
fluidRow(
column(
width = sidebarWidth,
class = "sidebar"
),
column(
width = 12 - sidebarWidth,
...
)
)
)
}
sidebar <- function(...){
div(
style = "display: none;",
tags$form(
class = "well",
id = "sidebar",
...
)
)
}
ui <- fluidPage(
useShinyjs(),
div( #~~ Main panel ~~#
navbarPage(
"Old Faithful Geyser Data",
tabPanel2(
"Plot",
plotOutput("ggplot")
),
tabPanel2(
"Summary",
verbatimTextOutput("summary")
),
tabPanel(
"Table",
fluidRow(
column(
width = 4,
wellPanel(
tags$fieldset(
tags$legend(h3("About")),
p("This app is cool")
)
)
),
column(
width = 8,
tableOutput("table")
)
)
),
id = "navbar"
)
),
sidebar( #~~ Sidebar ~~#
sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30)
)
)
server <- function(input, output) {
output[["ggplot"]] <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
output[["summary"]] <- renderPrint({
list(a = 1:10, b = 1:10)
})
output[["table"]] <- renderTable({
iris[1:10,]
})
observeEvent(input[["navbar"]], {
selector <-
sprintf("$('div.tab-pane[data-value=\"%s\"] div.sidebar')", input[["navbar"]])
append <- "selector.append($('#sidebar'));"
js <- sprintf("var selector=%s; if(selector.length){%s;}", selector, append)
runjs(js)
})
}
shinyApp(ui = ui, server = server)
Related
Is there a way to clear the tectinput after clicking the button. I tried with below code but does not work.
Open the application and fill the textinput. But after clicking the button, it does not clear
library(shiny)
library(shinyjs)
runApp(
list(
ui = shinyUI(fluidPage(theme = "bootstrap.css",
fluidRow(
column(8, align="center", offset = 2,
# selectInput("sdf","Asdf", choices = c(1,2), selected = NULL),
textInput("string", label="Input",value = "", width = "100%"),
tags$style("#string { height: 50px; width: 100%; text-align:center; font-size: 15px; display: block;}")
)
),
fluidRow(
column(6, align="center", offset = 3,
actionButton("button",label = "Clear"),
tags$style(type='text/css', "#button { vertical-align: middle; height: 50px; width: 100%; font-size: 30px;}")
)
)
)
), server = shinyServer(function(input, output) {
observeEvent(input$button,{
shinyjs::runjs("document.getElementById('string').reset();")
})
})))
What about updateTextInput?:
library(shiny)
library(shinyjs)
runApp(list(ui = shinyUI(
fluidPage(theme = "bootstrap.css",
fluidRow(
column(
8,
align = "center",
offset = 2,
# selectInput("sdf","Asdf", choices = c(1,2), selected = NULL),
textInput(
"string",
label = "Input",
value = "",
width = "100%"
),
tags$style(
"#string { height: 50px; width: 100%; text-align:center; font-size: 15px; display: block;}"
)
)
),
fluidRow(
column(
6,
align = "center",
offset = 3,
actionButton("button", label = "Clear"),
tags$style(
type = 'text/css',
"#button { vertical-align: middle; height: 50px; width: 100%; font-size: 30px;}"
)
)
))
), server = shinyServer(function(input, output, session) {
observeEvent(input$button, {
updateTextInput(session, inputId = "string", value = "")
})
})))
I try to run shiny app and get error:
fluidRow(column(3, : argument is missing, with no default
I have two tabPanel. I check code and there is no missing argument, no additional comma etc. when I run some portion of this code it works. I can't guess source of this error. How to deal with this problem?
Bellow you can see one part of UI-side code of this shiny app:
ui <- fluidPage(
theme = shinytheme("slate"),
tabsetPanel(
tabPanel(h4("TOP N' financial"),
fluidRow(
column(3,
selectInput("bank", "Choose Bank", choices = unique(BANKS$BANK)),
numericInput("Number", "Choose top N borrower", value = 10, min = 1),
tags$style(type='text/css', ".selectize-input { font-size: 10px; line-height: 1px;}
.selectize-dropdown { font-size: 10px; line-height: 10px; }"),
selectizeInput("excl","SELECT Columns", colname, multiple=T)
),
column(9,
div(dataTableOutput("topNtable"), style = "font-size: 75%; width: 75%")
)
),
br(),
fluidRow(style = "background-color:#4d3a7d;",
h4("Ratio Calculation and plotting"),
column(7,
style = "border: 4px ridge #01B392;",
fluidRow(
textInput("client_ID", "Insert Client ID", value = '204885044'),
uiOutput("ID_table")
),
fluidRow(style = "border: 4px ridge #FF8902;",
plotlyOutput('coef_plot'))
),
column(5,
style = "border: 4px double red;",
h4('simple calculator'),
fluidRow(
tags$style(type='text/css', ".selectize-input { font-size: 10px; line-height: 1px;}
.selectize-dropdown { font-size: 10px; line-height: 10px; }"),
column(6,
selectInput("num", "Numerator", num_cols, selected = 'TOTAL_ASSETS')),
column(6,
selectInput("denum", "denumerator", num_cols, selected = 'TOTAL_LIABILITIES'))
),
fluidRow(
column(6, align = "center",
div(tableOutput("coefficient"))#,style = "font-size: 75%; width: 75%")
)
)
)
),
This is server code (whole)
server <- function(input, output, session){
react_fin <- reactive(get_top_N_financial(input$bank,input$Number, input$excl)%>%
group_by(CLIENT_ID)%>%
filter(DATE == max(DATE))%>%
head(n=input$Number))
output$topNtable <- renderDataTable(react_fin())
#CALCULATOR
calc_d <- reactive(get_top_N_financial(input$bank,input$Number, c(num_cols,'DATE')))
coef_d <- reactive({
req(input$client_ID)
filter(calc_d(), CLIENT_ID == input$client_ID)
})
coef_df <- reactive({
req(input$num)
req(input$denum)
ratio <- round(coef_d()[[input$num]]/coef_d()[[input$denum]], 4)
data.frame(cbind(ratio, coef_d()$DATE))%>%
rename('Date'=V2)%>%
arrange(desc(Date))
})
output$coefficient <- renderTable(coef_df()%>%
slice_head(n = 6))
output$coef_plot <-renderPlotly(plot_ly(coef_df(), x = ~Date,
y = ~ratio, type = 'scatter', mode = 'lines',
height = 300, width = 500))
Client <- reactive((get_name(input$bank, input$client_ID)))
output$ID_table <- renderUI(Client()$CLIENT_NAME)
pd_dt <- reactive(get_debt(input$client_ID))
output$ecl_pl <- renderPlot(plot_pd_lgd(pd_dt()))
## create ind_table
observeEvent(pd_dt(), {
choices <- unique(pd_dt()$DATE)
updateSelectInput(inputId = "ind_date", choices = choices)
})
output$ind_table <-function()(ind_sum_tbl(pd_dt(), input$ind_date))
################# PAGE2 #################
d_PD_PTI <- reactive(get_PD_PTI(input$N, input$start, input$end, input$stage))
output$reg_pl <- renderPlot(PD_PTI_Bank(d_PD_PTI()))
d_PD_Box <- reactive(get_PD_Box(input$N,input$stage))
output$Box_pd <- renderPlot(PD_Box(d_PD_Box()))
output$PTI_PD <- renderPlot(PD_PTI_heat(d_PD_PTI()))
d_LGD_LTV <- reactive(get_LGD_LTV(input$N, input$start, input$end,input$stage))
output$LTV_LGD <- renderPlot(LGD_LTV_heat(d_LGD_LTV()))
output$PD_Client_scatter <- renderPlotly(PD_diff_banks_scatter(input$start, input$end, input$N))
df <- reactive(PD_CLIENT_DF(input$start, input$end,input$N))
output$PD_scat <- renderPlot(PD_CLIENT_plot(df()))
output$brush_tbl <-renderTable({
brushedPoints(df(), input$brush) %>%
select()
})
}
Thanks in advance
My guess: Issue of parentheses: Here is a working example with your UI code.
library(shiny)
library(shinythemes)
BANKS
ui <- fluidPage(
theme = shinytheme("slate"),
tabsetPanel(
tabPanel(h4("TOP N' financial"),
fluidRow(
column(3,
selectInput("bank", "Choose Bank", choices = unique(BANKS$BANK)),
numericInput("Number", "Choose top N borrower", value = 10, min = 1),
tags$style(type='text/css', ".selectize-input { font-size: 10px; line-height: 1px;}
.selectize-dropdown { font-size: 10px; line-height: 10px; }"),
selectizeInput("excl","SELECT Columns", colname, multiple=T)
),
column(9,
div(dataTableOutput("topNtable"), style = "font-size: 75%; width: 75%")
)
)
)
)
),
server <- function(input, output, session) {
}
shinyApp(ui, server)
Using a Shiny app, I would like to implement a slider with slickR to switch from one image to the other.
I managed to implement the slider but I'm having trouble in displaying the images correctly because of their different sizes.
In the following example, the stackexchange logo is way bigger than the stackoverflow logo. When displaying them with slickR(), the bigger logo makes inroads into the first one like this:
I would also like to have the size of the pictures relative to the size of the screen.
Here is a reproducible example of the Shiny app used to generate the above image:
library(shiny)
library(slickR)
# User interface ----
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
),
mainPanel(
slickROutput("slickr", width = "auto")
)
)
)
# Server ----
server <- function(input, output) {
imgs_links <- list(
"https://upload.wikimedia.org/wikipedia/fr/9/95/Stack_Overflow_website_logo.png",
"https://upload.wikimedia.org/wikipedia/commons/6/6f/Stack_Exchange_Logo.png")
output$slickr <- renderSlickR({
photo_list <- lapply(imgs_links, function(x){
tags$div(
tags$img(src = x, width = "10%", height = "10%")
)
})
imgs <- do.call(tagList, photo_list)
slickR(imgs)
})
}
# Run the application ----
shinyApp(ui = ui, server = server)
What would be the correct way to have each image resized according the size of the screen?
I don't manage to get it with the 'slickR' package. Here is a solution which doesn't use this package, it uses the 'slick' JavaScript library. You have to download the library files and put them in the www/slick-1.8.1/slick folder.
library(shiny)
ui <- fluidPage(
tags$head(
tags$link(rel="stylesheet", type="text/css",
href="slick-1.8.1/slick/slick-theme.css"),
tags$link(rel="stylesheet", type="text/css",
href="slick-1.8.1/slick/slick.css"),
tags$script(type="text/javascript",
src="slick-1.8.1/slick/slick.js"),
tags$script(HTML(
"$(document).ready(function(){
$('#images').slick({
arrows: true,
dots:true
});
});")),
tags$style(HTML(
"#images .slick-prev {
position:absolute;
top:65px;
left:-50px;
}
#images .slick-next {
position:absolute;
top:95px;
left:-50px;
}
.slick-prev:before, .slick-next:before {
color:red !important;
font-size: 30px;
}
.content {
margin: auto;
padding: 2px;
width: 90%;
}"))
),
sidebarLayout(
sidebarPanel(
####
),
mainPanel(
tags$div(
class = "content",
tags$div(
id = "images",
tags$img(
src = "https://upload.wikimedia.org/wikipedia/fr/9/95/Stack_Overflow_website_logo.png",
width = "50vw"
),
tags$img(
src = "https://upload.wikimedia.org/wikipedia/commons/6/6f/Stack_Exchange_Logo.png",
width = "50vw"
)
)
)
)
)
)
server <- function(input, output) {
}
# Run the application
shinyApp(ui = ui, server = server)
EDIT: dynamic number of images
library(shiny)
ui <- fluidPage(
tags$head(
tags$link(rel="stylesheet", type="text/css",
href="slick-1.8.1/slick/slick-theme.css"),
tags$link(rel="stylesheet", type="text/css",
href="slick-1.8.1/slick/slick.css"),
tags$script(type="text/javascript",
src="slick-1.8.1/slick/slick.js"),
tags$style(HTML(
"#carousel .slick-prev {
position:absolute;
top:65px;
left:-50px;
}
#carousel .slick-next {
position:absolute;
top:95px;
left:-50px;
}
.slick-prev:before, .slick-next:before {
color:red !important;
font-size: 30px;
}
.content {
margin: auto;
padding: 2px;
width: 90%;
}"))
),
sidebarLayout(
sidebarPanel(
checkboxGroupInput(
"images",
"Select images",
choiceNames = c("Stackoverflow", "Stackexchange", "Asymptote"),
choiceValues = c(
"https://upload.wikimedia.org/wikipedia/fr/9/95/Stack_Overflow_website_logo.png",
"https://upload.wikimedia.org/wikipedia/commons/6/6f/Stack_Exchange_Logo.png",
"https://www.clipartmax.com/png/small/203-2038151_asymptote-vector-graphics-language-wikipedia-rh-en-asymptote.png"
)
)
),
mainPanel(
tags$div(
class = "content",
uiOutput("carousel-ui"),
)
)
)
)
server <- function(input, output) {
output[["carousel-ui"]] <- renderUI({
imgs <- lapply(input[["images"]], function(x){
tags$img(src = x, width = "50vw")
})
imgs_div <- do.call(function(...) div(id = "carousel", ...), imgs)
script <- tags$script(HTML(
"$('#carousel').slick({
arrows: true,
dots:true
});"))
do.call(tagList, list(imgs_div, script))
})
}
# Run the application
shinyApp(ui = ui, server = server)
I have some plots in a panel. I want to change them into tabsetpanel when the window width is small. Is there any way in shiny to determine window width of browser. For example, in the following example, how can I switch uiOutput from plotPanel1 to plotPanel2 when the window width is large enough.
library(ggplot2)
ui <- fluidPage(
title = "TestApp",
h1("Test Application"),
sidebarLayout(
sidebarPanel(
sliderInput("bins", "Bins", 2, 20, 1, value = 10)
),
mainPanel(
fluidRow(
uiOutput("plotPanel1")
)
)
)
)
server <- function(input, output, session){
output$plot1 <- renderPlot({
mdl <- lm(mpg ~ ., data = mtcars)
ggplot(mdl, aes(.resid)) + geom_histogram(bins = input$bins)
}, res = 110)
output$plot2 <- renderPlot({
mdl <- lm(UrbanPop ~ ., data = USArrests)
ggplot(mdl, aes(.resid)) + geom_histogram(bins = input$bins)
}, res = 110)
output$plot3 <- renderPlot({
mdl <- lm(uptake ~ ., data = CO2)
ggplot(mdl, aes(.resid)) + geom_histogram(bins = input$bins)
}, res = 110)
output$plotPanel1 <- renderUI({
tabsetPanel(
tabPanel(
"plot1",
plotOutput("plot1")
),
tabPanel(
"plot2",
plotOutput("plot2")
),
tabPanel(
"plot3",
plotOutput("plot3")
)
)
})
output$plotPanel2 <- renderUI({
fluidRow(
column(
4,
plotOutput("plot1")
),
column(
4,
plotOutput("plot2")
),
column(
4,
plotOutput("plot3")
)
)
})
}
runApp(shinyApp(ui, server))
Since Shiny is generating a bunch of HTML you could use media-query, or another possibility is to use javaScript and get the width of the window. I had some trouble with the css solution, but I will show you both:
Approach #1 (Working): Using javaScript
With javaScript you can define an input element based on the width of the window:
tags$head(tags$script('
var width = 0;
$(document).on("shiny:connected", function(e) {
width = window.innerWidth;
Shiny.onInputChange("width", width);
});
$(window).resize(function(e) {
width = window.innerWidth;
Shiny.onInputChange("width", width);
});
'))
If this script is included in the UI, you can then access input$width to obtain the width of the window. (Disclaimer: I used the accepted answer in the following SO topic for the JS code.)
I added an observer to check the width. If it is below/above a certain threshold then the elements are shown/hidden.
observe( {
req(input$width)
if(input$width < 800) {
shinyjs::show("plotPanel1")
shinyjs::hide("plotPanel2")
} else {
shinyjs::hide("plotPanel1")
shinyjs::show("plotPanel2")
}
})
Full code:
library(shinyjs)
library(ggplot2)
ui <- fluidPage(
useShinyjs(),
title = "TestApp",
h1("Test Application"),
sidebarLayout(
sidebarPanel(
sliderInput("bins", "Bins", 2, 20, 1, value = 10)
),
mainPanel(
fluidRow(
div(id="p1", uiOutput("plotPanel1")),
div(id="p2", uiOutput("plotPanel2"))
)
)
),
tags$head(tags$script('
var width = 0;
$(document).on("shiny:connected", function(e) {
width = window.innerWidth;
Shiny.onInputChange("width", width);
});
$(window).resize(function(e) {
width = window.innerWidth;
Shiny.onInputChange("width", width);
});
'))
)
server <- function(input, output, session){
plot1 <- reactive({
ggplot(lm(mpg ~ ., data = mtcars), aes(.resid)) +
geom_histogram(bins = input$bins)
})
plot2 <- reactive({
ggplot(lm(UrbanPop ~ ., data = USArrests), aes(.resid)) +
geom_histogram(bins = input$bins)
})
plot3 <- reactive({
ggplot(lm(uptake ~ ., data = CO2), aes(.resid)) +
geom_histogram(bins = input$bins)
})
output$plotPanel1 <- renderUI({
tagList(
tabsetPanel(
tabPanel(
"plot1",
renderPlot(plot1())
),
tabPanel(
"plot2",
renderPlot(plot2())
),
tabPanel(
"plot3",
renderPlot(plot3())
)
)
)
})
output$plotPanel2 <- renderUI({
tagList(
fluidRow(
column(
4,
renderPlot(plot1())
),
column(
4,
renderPlot(plot2())
),
column(
4,
renderPlot(plot3())
)
)
)
})
observe( {
req(input$width)
if(input$width < 800) {
shinyjs::show("plotPanel1")
shinyjs::hide("plotPanel2")
} else {
shinyjs::hide("plotPanel1")
shinyjs::show("plotPanel2")
}
})
}
runApp(shinyApp(ui, server))
This is not a perfect solution in my opinion, since we are rendering every plot twice, however you can probably build on this.
Approach #2 (NOT working): CSS and media-query
You can control the display attribute within a media-query in tags$head. It works fine for any element, however I found that it doesn't work well with UIOutput.
Working example for simple div with text:
ui <- fluidPage(
tags$head(
tags$style(HTML("
#media screen and (min-width: 1000px) {
#p1 {
display: none;
}
#p2 {
display: block;
}
}
#media screen and (max-width: 1000px) {
#p1 {
display: block;
}
#p2 {
display: none;
}
}
"
))
),
div(id="p1", "First element"),
div(id="p2", "Second element")
)
Not working example for UIOutput:
ui <- fluidPage(
title = "TestApp",
h1("Test Application"),
sidebarLayout(
sidebarPanel(
sliderInput("bins", "Bins", 2, 20, 1, value = 10)
),
mainPanel(
fluidRow(
div(id="p1", uiOutput("plotPanel1")),
div(id="p2", uiOutput("plotPanel2"))
)
)
),
tags$head(
tags$style(HTML("
#media screen and (min-width: 1000px) {
#plotPanel1 {
display: none;
}
#plotPanel2 {
display: block;
}
}
#media screen and (max-width: 1000px) {
#plotPanel1 {
display: block;
}
#plotPanel2 {
display: none;
}
}
"
))
)
)
server <- function(input, output, session){
plot1 <- reactive({
ggplot(lm(mpg ~ ., data = mtcars), aes(.resid)) +
geom_histogram(bins = input$bins)
})
plot2 <- reactive({
ggplot(lm(UrbanPop ~ ., data = USArrests), aes(.resid)) +
geom_histogram(bins = input$bins)
})
plot3 <- reactive({
ggplot(lm(uptake ~ ., data = CO2), aes(.resid)) +
geom_histogram(bins = input$bins)
})
output$plotPanel1 <- renderUI({
tagList(
tabsetPanel(
tabPanel(
"plot1",
renderPlot(plot1())
),
tabPanel(
"plot2",
renderPlot(plot2())
),
tabPanel(
"plot3",
renderPlot(plot3())
)
)
)
})
output$plotPanel2 <- renderUI({
tagList(
fluidRow(
column(
4,
renderPlot(plot1())
),
column(
4,
renderPlot(plot2())
),
column(
4,
renderPlot(plot3())
)
)
)
})
}
runApp(shinyApp(ui, server))
I have a shiny app (using navbarPage) with many tabs and would like to add a sidebarMenu that can be seen no matter which tab is selected. The input values in the sidebar have an impact on the content of all tabs.
Additionally, it should be possible to hide the sidebarMenu as it is in a shinydashboard.
I see two possible ways:
(A) Using shinydashboard and somehow adding a top navigation bar or
(B) using navbarPage and somehow adding a sidebar menu that can be hidden.
(A) Using shinydashboard, the closest to what I want is this (simplified MWE):
library("shiny")
library("shinydashboard")
cases <- list(A=seq(50,500, length.out=10), B=seq(1000,10000, length.out=10))
ui <- dashboardPage(
dashboardHeader(title = "dash w/ navbarMenu"),
dashboardSidebar(selectizeInput('case', 'Pick a case', selected="A", choices = c("A", "B"), multiple = FALSE), numericInput('num', 'Number', min = 1, max = 10, value = 1, step = 1)),
dashboardBody(
tabsetPanel(
tabPanel(h4("Perspective 1"),
tabsetPanel(
tabPanel("Subtab 1.1", plotOutput("plot11")),
tabPanel("Subtab 1.2")
)),
tabPanel(h4("Perspective 2"),
tabsetPanel(
tabPanel("Subtab 2.1"),
tabPanel("Subtab 2.2")
))
)
)
)
server <- function(input, output) {
output$plot11 <- renderPlot({
hist(rnorm(cases[[input$case]][input$num]))
})
}
shinyApp(ui, server)
which is ugly because the navigation bar menu are tabsets which are not part of the menu. What I want is:
Based on this post, I guess it's not possible to include "Perspective 1" and "Perspective 2" tabs in the top menu at all, thus using shinydashboard seems not feasible.
(B) Using navbarPage, I tried using navlistPanel() but I didn't succeed to
(1) make it behave like a sidebarMenu, i.e. be overall visible on the left side of the page and
(2) add hide functionality. Here is my try:
library("shiny")
cases <- list(A=seq(50,500, length.out=10),
B=seq(1000,10000, length.out=10))
ui <- navbarPage(title = "nav w/ sidebarMenu",
tabPanel(h4("Perspective 1"),
tabsetPanel(
tabPanel("Subtab 1.1",
plotOutput("plot11")),
tabPanel("Subtab 1.2")
)),
tabPanel(h4("Perspective 2"),
tabsetPanel(
tabPanel("Subtab 2.1"),
tabPanel("Subtab 2.2")
)),
navlistPanel(widths = c(2, 2), "SidebarMenu",
tabPanel(selectizeInput('case', 'Pick a case', selected="A", choices = c("A", "B"), multiple = FALSE)),
tabPanel(numericInput('num', 'Number', min = 1, max = 10, value = 1, step = 1))
)
)
server <- function(input, output) {
output$plot11 <- renderPlot({
hist(rnorm(cases[[input$case]][input$num]))
})
}
shinyApp(ui, server)
Again, what I want is:
I know, there is flexDashboard. It does not solve the problem for three reasons:
(1) I think it is not possible to hide the sidebar menu, as it is a column and not a real sidebar menu,
(2) it is not reactive which I require in my app,
(3) I think dataTables don't work, which I also need.
Besides, I'd prefer to not have to change the code to Rmarkdown syntax.
Preferably, I'd use a navbarPage and add a sidebarMenu, because my app is already built using navbarPage.
You could use sidebarLayout and do something like this:
ui <- fluidPage(sidebarLayout(
sidebarPanel(navlistPanel(
widths = c(12, 12), "SidebarMenu",
tabPanel(selectizeInput('case', 'Pick a case', selected="A", choices = c("A", "B"), multiple = FALSE)),
tabPanel(numericInput('num', 'Number', min = 1, max = 10, value = 1, step = 1))
)),
mainPanel(navbarPage(title = "nav w/ sidebarMenu",
tabPanel(h4("Perspective 1"),
tabsetPanel(
tabPanel("Subtab 1.1",
plotOutput("plot11")),
tabPanel("Subtab 1.2")
)),
tabPanel(h4("Perspective 2"),
tabsetPanel(
tabPanel("Subtab 2.1"),
tabPanel("Subtab 2.2")
)))
)
))
You get something like this:
Another option would be using fluidRow function. Something like this:
ui <- fluidPage(
fluidRow(
column(3, navlistPanel(
widths = c(12, 12), "SidebarMenu",
tabPanel(selectizeInput('case', 'Pick a case', selected="A", choices = c("A", "B"), multiple = FALSE)),
tabPanel(numericInput('num', 'Number', min = 1, max = 10, value = 1, step = 1))
)),
column(9, navbarPage(title = "nav w/ sidebarMenu",
tabPanel(h4("Perspective 1"),
tabsetPanel(
tabPanel("Subtab 1.1",
plotOutput("plot11")),
tabPanel("Subtab 1.2")
)),
tabPanel(h4("Perspective 2"),
tabsetPanel(
tabPanel("Subtab 2.1"),
tabPanel("Subtab 2.2")
))))
)
)
To get this:
Hope it helps!
This is now possible using bootstraplib
Github Request to implement this:
https://github.com/rstudio/bootstraplib/issues/76
min reprex:
# package load ------------------------------------------------------------
library(shiny)
library(bootstraplib)
# boot dash layout funs ---------------------------------------------------
boot_side_layout <- function(...) {
div(class = "d-flex wrapper", ...)
}
boot_sidebar <- function(...) {
div(
class = "bg-light border-right sidebar-wrapper",
div(class = "list-group list-group-flush", ...)
)
}
boot_main <- function(...) {
div(
class = "page-content-wrapper",
div(class = "container-fluid", ...)
)
}
# title -------------------------------------------------------------------
html_title <-
'<span class="logo">
<div style="display:inline-block;">
<img src="https://jeroen.github.io/images/Rlogo.png" height="35"/>
<b>my company name</b> a subtitle of application or dashboard
</div>
</span>'
# css ---------------------------------------------------------------------
css_def <- "
body {
overflow-x: hidden;
}
.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
padding-left: 0px;
}
.sidebar-wrapper {
min-height: 100vh;
margin-left: -15rem;
padding-left: 15px;
padding-right: 15px;
-webkit-transition: margin .25s ease-out;
-moz-transition: margin .25s ease-out;
-o-transition: margin .25s ease-out;
transition: margin .25s ease-out;
}
.sidebar-wrapper .list-group {
width: 15rem;
}
.page-content-wrapper {
min-width: 100vw;
padding: 20px;
}
.wrapper.toggled .sidebar-wrapper {
margin-left: 0;
}
.sidebar-wrapper, .page-content-wrapper {
padding-top: 20px;
}
.navbar{
margin-bottom: 0px;
}
#media (max-width: 768px) {
.sidebar-wrapper {
padding-right: 0px;
padding-left: 0px;
}
}
#media (min-width: 768px) {
.sidebar-wrapper {
margin-left: 0;
}
.page-content-wrapper {
min-width: 0;
width: 100%;
}
.wrapper.toggled .sidebar-wrapper {
margin-left: -15rem;
}
}
"
# app ---------------------------------------------------------------------
ui <- tagList(
tags$head(tags$style(HTML(css_def))),
bootstrap(),
navbarPage(
collapsible = TRUE,
title = HTML(html_title),
tabPanel(
"Tab 1",
boot_side_layout(
boot_sidebar(
sliderInput(
inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30
)
),
boot_main(
fluidRow(column(6, h1("Plot 1")), column(6, h1("Plot 2"))),
fluidRow(
column(6, plotOutput(outputId = "distPlot")),
column(6, plotOutput(outputId = "distPlot2"))
)
)
)
),
tabPanel(
"Tab 2",
boot_side_layout(
boot_sidebar(h1("sidebar input")),
boot_main(h1("main output"))
)
)
)
)
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x,
breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times"
)
})
output$distPlot2 <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x,
breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times"
)
})
}
shinyApp(ui, server)