I try to make a loading screen as in this nice example http://daattali.com:3838/loading-screen/. Unfortunately I cannot figure out how to do exactly the same effect with 'navbarPage'.
In this slightly modified app below there are two tab panels called "start" and "end". When the app starts none of the tab panels are active. One have to quickly click on the first tab to see the loading screen but this is not what I would like. Is there a way to make it so quick and easy as in the mentioned example?
Thank you for the help!
library(shinyjs)
appCSS <- "
#loading-content {
position: absolute;
background: #000000;
opacity: 0.9;
z-index: 100;
left: 0;
right: 0;
height: 100%;
text-align: center;
color: #FFFFFF;
}
"
shinyApp(
ui = navbarPage(
useShinyjs(),
inlineCSS(appCSS),
tabPanel(title = "Start",
# Loading message
div(
id = "loading-content",
h2("Loading...")
),
# The main app code goes here
div(
id = "app-content",
p("This is a simple example of a Shiny app with a loading screen."),
p("You can view the source code",
tags$a(href = 'https://github.com/daattali/shiny-server/blob/master/loading-screen',
"on GitHub")
)
)
),
tabPanel(title = "End",
h2("Second tab"))
),
server = function(input, output, session) {
# Simulate work being done for 1 second
Sys.sleep(2)
# Hide the loading message when the rest of the server function has executed
hide(id = "loading-content", anim = TRUE, animType = "fade")
}
)
EDIT: The original link to the loading screen app has been taken down. It can now be accessed on github here
Well, I believe that you will enjoy with this solution but it is not perfect. The key is the tagList, you can add whatever you want before the navbar.
Furthermore I add the padding to your CSS code and now, there is a title in the navbar.
Unfortunately the navbarPage is not possible to hide of a not complex way.
library(shiny)
library(shinyjs)
appCSS <- "
#loading-content {
position: absolute;
padding: 10% 0 0 0;
background: #000000;
opacity: 0.9;
z-index: 100;
left: 0;
right: 0;
height: 100%;
text-align: center;
color: #FFFFFF;
}
"
shinyApp(
ui =
tagList(
useShinyjs(),
inlineCSS(appCSS),
# Loading message
div(
id = "loading-content",
h2("Loading...")
),
navbarPage("Test",
tabPanel(title = "Start",
# The main app code goes here
div(
id = "app-content",
p("This is a simple example of a Shiny app with a loading screen."),
p("You can view the source code",
tags$a(href = 'https://github.com/daattali/shiny-server/blob/master/loading-screen',
"on GitHub")
)
)
),
tabPanel(title = "End",
h2("Second tab"))
) #close navbarPage
), #close tagList
server = function(input, output, session) {
# Simulate work being done for 1 second
Sys.sleep(5)
# Hide the loading message when the rest of the server function has executed
hide(id = "loading-content", anim = TRUE, animType = "fade")
}
)
Related
The Shinyglide package is just what I need, using a carousel for grouped radio buttons giving the user many choices for data parsing.
However, the "Next" (and "Back") button occupies a large white space. I'd like to shift the button in line with the glide row (see image at bottom). Does anyone know how to do this? Is there a CSS trick? Reading through the Glide manual, the only choices are "top" and "bottom".
If moving the Next/Back button isn't possible, a secondary option is to insert (a somewhat superfluous) line of text but in line with the Next/Back buttons, to at least cover up the annoyingly large white space.
The actual panel this is for has much more information presented than in this example, so I'm trying to make the page as clean as possible.
Please see image at bottom that better explains what I'm trying to do.
Reproducible example:
library(dplyr)
library(DT)
library(shiny)
library(shinyglide)
ui <-
fluidPage(
fluidRow(div(style = "margin-top:15px"),
strong("Input choices shown in row below, click ´Next´ to see more choices:"),
column(12, glide(
height = "25",
controls_position = "top",
screen(
div(style = "margin-top:10px"),
wellPanel(
radioButtons(inputId = 'group1',
label = NULL,
choiceNames = c('By period','By MOA'),
choiceValues = c('Period','MOA'),
selected = 'Period',
inline = TRUE
),
style = "padding-top: 12px; padding-bottom: 0px;"
)
),
screen(
div(style = "margin-top:10px"),
wellPanel(
radioButtons(inputId = 'group2',
label = NULL,
choiceNames = c('Exclude CT','Include CT'),
choiceValues = c('Exclude','Include'),
selected = 'Exclude',
inline = TRUE
),
style = "padding-top: 12px; padding-bottom: 0px;"
)
)
)
)
),
DTOutput("plants")
)
server <- function(input, output, session) {
output$plants <- renderDT({iris %>% datatable(rownames = FALSE)})
}
shinyApp(ui, server)
You could use a custom control element with custom_controls, and then have it hover over the displayed screen on the top right with a container set to absolute positioning. Setting a limited width for the container will ensure that the back button won't fly too far out.
Something along these lines:
glide(custom_controls = div(class = "glide-controls", glideControls()), ...)
# Somewhere in the UI
tags$style(
".glide-controls { position: absolute; top: 18px; right: 15px; width: 160px; }"
)
Just make sure to also set controls_position = "bottom" so that the controls hover over the screen content, rather than under it.
A minimal example app:
library(shiny)
library(shinyglide)
ui <- fixedPage(
h3("Simple shinyglide app"),
tags$style(
".glide-controls { position: absolute; top: 18px; right: 15px; width: 160px; }"
),
glide(
custom_controls = div(class = "glide-controls", glideControls()),
screen(wellPanel(p("First screen."))),
screen(wellPanel(p("Second screen.")))
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
I am trying to create a shiny app that contains a series of double-radio-button questions. The relevant codes are as below:
fluidRow(column(12, align='center',h4(tags$style(HTML(".radio-inline {margin-right: 200px;}"))), radioButtons("MPL1",label="",choiceNames=c("(60, 0; 12, 1)","(56, 0; 48, 1)"), choiceValues=c(0,1),selected= character(0),inline=T))),
It works, but I am stuck with adjusting the locations of the buttons. As the below figure shows, the default setting always puts the button at the left-hand side of its label, while I am trying to put the buttons against each other.
Could you please tell me how to achieve my expectation? Besides, it would be very lovely if you could tell me how I can systematically study css setting. I am eager to be an expert in making shiny apps. Thank you very much.
Best,
J
Here you go. I added two more rules to make this happen.
library(shiny)
ui <- fluidPage(
fluidRow(
column(12, align='center',
tags$style(HTML(
"
.radio-inline {margin-right: 200px;}
.radio-inline:nth-of-type(odd) > span {
padding-right: 25px;
}
.radio-inline:nth-of-type(odd) > input {
position: absolute;
right: 0;
}
"
)),
radioButtons(
"MPL1", label="",
choiceNames=c("(60, 0; 12, 1)","(56, 0; 48, 1)"),
choiceValues=c(0,1),
selected= character(0),
inline=TRUE
)
)
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
To learn CSS, use W3 school
I have a basic R shiny app that I would like to build a light/ dark mode switch for. I think if I can just get it working for the table tab it should be fine to do for the rest. I am aware that shinyjs is the best way to go about it but I can't seem to find the code anywhere.
library(dplyr)
library(shiny)
library(shinythemes)
ui <- fluidPage(theme = shinytheme("slate"),
tags$head(tags$style(HTML(
"
.dataTables_length label,
.dataTables_filter label,
.dataTables_info {
color: white!important;
}
.paginate_button {
background: white!important;
}
thead {
color: white;
}
"))),
mainPanel(tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
)))
server <- function(input, output) {
output$table <- DT::renderDT({
iris
})
}
shinyApp(ui = ui, server = server)
EDITED: see notes at the end
If you want to use bootstrap themes, it's possible to do this using a checkbox input and a javascript event that adds/removes <link> elements (i.e., the html element that loads the bootstrap css theme). I switched the shinytheme to darkly as there's a corresponding light theme (flatly). I removed the css that you defined in tags$head as that will be added/removed based on the theme toggle. (see full example below)
Even though this works, there are likely performance issues. Be aware that each time the theme is changed, the file is fetched and reloaded into the browser. There are also style differences between themes, this may cause content to be reorganized or moved slightly when new theme is applied (this may be disruptive for the user). If you were to choose this approach, I would recommend finding a well-designed light and dark theme combo.
Alternatively, you can select a basic bootstrap theme and define your own css themes. You can use a toggle (like this example) or the media query prefers-color-scheme. Then the shinyjs class functions, you can toggle themes from the R server. This approach is often recommended, but does take a bit longer to develop and validate.
Using the bootstrap approach, here's how you could switch themes.
app.R
In the ui, I created a checkbox input and placed it as the last element (for example purposes).
checkboxInput(
inputId = "themeToggle",
label = icon("sun")
)
JS
To switch the bootstrap themes, I defined the html dependency paths defined by the shinythemes package. You can find these in your R package library (library/shinythemes/).
const themes = {
dark: 'shinythemes/css/darkly.min.css',
light: 'shinythemes/css/flatly.min.css'
}
To load a new theme, the paths need to be rendered as an html element. We will also need a function that removes an existing css theme. The easiest way to do that is to select the element that has a matching href as defined in the themes variable.
// create new <link>
function newLink(theme) {
let el = document.createElement('link');
el.setAttribute('rel', 'stylesheet');
el.setAttribute('text', 'text/css');
el.setAttribute('href', theme);
return el;
}
// remove <link> by matching the href attribute
function removeLink(theme) {
let el = document.querySelector(`link[href='${theme}']`)
return el.parentNode.removeChild(el);
}
I also removed the styles defined in the tags$head and created a new <style> element in js.
// css themes (originally defined in tags$head)
const extraDarkThemeCSS = ".dataTables_length label, .dataTables_filter label, .dataTables_info { color: white!important;} .paginate_button { background: white!important;} thead { color: white;}"
// create new <style> and append css
const extraDarkThemeElement = document.createElement("style");
extraDarkThemeElement.appendChild(document.createTextNode(extraDarkThemeCSS));
// add element to <head>
head.appendChild(extraDarkThemeElement);
Lastly, I created an event and attached it to the checkbox input. In this example, checked = 'light' and unchecked = 'dark'.
toggle.addEventListener('input', function(event) {
// if checked, switch to light theme
if (toggle.checked) {
removeLink(themes.dark);
head.removeChild(extraDarkThemeElement);
head.appendChild(lightTheme);
} else {
// else add darktheme
removeLink(themes.light);
head.appendChild(extraDarkThemeElement)
head.appendChild(darkTheme);
}
})
Here's the full app.R file.
library(dplyr)
library(shiny)
library(shinythemes)
ui <- fluidPage(
theme = shinytheme("darkly"),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
checkboxInput(
inputId = "themeToggle",
label = icon("sun")
)
),
tags$script(
"
// define css theme filepaths
const themes = {
dark: 'shinythemes/css/darkly.min.css',
light: 'shinythemes/css/flatly.min.css'
}
// function that creates a new link element
function newLink(theme) {
let el = document.createElement('link');
el.setAttribute('rel', 'stylesheet');
el.setAttribute('text', 'text/css');
el.setAttribute('href', theme);
return el;
}
// function that remove <link> of current theme by href
function removeLink(theme) {
let el = document.querySelector(`link[href='${theme}']`)
return el.parentNode.removeChild(el);
}
// define vars
const darkTheme = newLink(themes.dark);
const lightTheme = newLink(themes.light);
const head = document.getElementsByTagName('head')[0];
const toggle = document.getElementById('themeToggle');
// define extra css and add as default
const extraDarkThemeCSS = '.dataTables_length label, .dataTables_filter label, .dataTables_info { color: white!important;} .paginate_button { background: white!important;} thead { color: white;}'
const extraDarkThemeElement = document.createElement('style');
extraDarkThemeElement.appendChild(document.createTextNode(extraDarkThemeCSS));
head.appendChild(extraDarkThemeElement);
// define event - checked === 'light'
toggle.addEventListener('input', function(event) {
// if checked, switch to light theme
if (toggle.checked) {
removeLink(themes.dark);
head.removeChild(extraDarkThemeElement);
head.appendChild(lightTheme);
} else {
// else add darktheme
removeLink(themes.light);
head.appendChild(extraDarkThemeElement)
head.appendChild(darkTheme);
}
})
"
)
)
server <- function(input, output) {
output$table <- DT::renderDT({
iris
})
}
shinyApp(ui, server)
EDITS
In this example, I used a checkBoxInput. You can "hide" the input using the following css class. I would recommend adding a visually hidden text element to make this element accessible. The UI would be changed to the following.
checkboxInput(
inputId = "themeToggle",
label = tagList(
tags$span(class = "visually-hidden", "toggle theme"),
tags$span(class = "fa fa-sun", `aria-hidden` = "true")
)
)
Then add the css following css. You can also select and style the icon using #themeToggle + span .fa-sun
/* styles for toggle and visually hidden */
#themeToggle, .visually-hidden {
position: absolute;
width: 1px;
height: 1px;
clip: rect(0 0 0 0);
clip: rect(0, 0, 0, 0);
overflow: hidden;
}
/* styles for icon */
#themeToggle + span .fa-sun {
font-size: 16pt;
}
Here's the updated ui. (I removed the js to make the example shorter)
ui <- fluidPage(
theme = shinytheme("darkly"),
tags$head(
tags$style(
"#themeToggle,
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
clip: rect(0 0 0 0);
clip: rect(0, 0, 0, 0);
overflow: hidden;
}",
"#themeToggle + span .fa-sun {
font-size: 16pt;
}"
)
),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
checkboxInput(
inputId = "themeToggle",
label = tagList(
tags$span(class = "visually-hidden", "toggle theme"),
tags$span(class = "fa fa-sun", `aria-hidden` = "true")
)
)
),
tags$script("...")
)
You can dynamically switch between bootstrap themes by downloading their CSS files from here, putting them into a folder in your project and using includeCSS in a dynamically generated UI chunk:
library(dplyr)
library(shiny)
library(shinythemes)
ui <- fluidPage(
theme = shinytheme("flatly"),
uiOutput("style"),
tags$head(
tags$style(
HTML(
"
.dataTables_length label,
.dataTables_filter label,
.dataTables_info {
color: white!important;
}
.paginate_button {
background: white!important;
}
thead {
color: white;
}
"
)
)
),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
checkboxInput("style", "Dark theme")
)
)
server <- function(input, output) {
output$table <- DT::renderDT({
iris
})
output$style <- renderUI({
if (!is.null(input$style)) {
if (input$style) {
includeCSS("www/darkly.css")
} else {
includeCSS("www/flatly.css")
}
}
})
}
shinyApp(ui = ui, server = server)
From what I understand, this will solve the problem.
The advantage of this approach is that if you remove the checkbox and then generate it again, it will still work. Personally, I was going to use dcruvolos helpful solution in my app until I realised that I can't use it with shiny.router because as soon as you temporarily remove the checkbox from the UI, the JS code stops working (if I understand correctly).
Here is a checkbox in the form of a uiOutput that you can add or remove and it will continue working:
library(dplyr)
library(shiny)
library(shinythemes)
ui <- fluidPage(
theme = shinytheme("flatly"),
uiOutput("style"),
tags$head(
tags$style(
HTML(
"
.dataTables_length label,
.dataTables_filter label,
.dataTables_info {
color: white!important;
}
.paginate_button {
background: white!important;
}
thead {
color: white;
}
"
)
)
),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
uiOutput("style_checkbox")
)
)
server <- function(input, output) {
output$table <- DT::renderDT({
iris
})
current_theme <- reactiveVal(FALSE)
output$style_checkbox <- renderUI({
checkboxInput("style", "Dark theme", value = current_theme())
})
output$style <- renderUI({
if (!is.null(input$style)) {
current_theme(input$style)
if (input$style) {
includeCSS("www/darkly.css")
} else {
includeCSS("www/flatly.css")
}
}
})
}
shinyApp(ui = ui, server = server)
[Edit]
I tried to simplify my code as much as possible:
server.R :
server <- function(input, output, session) {
output$body_UI<-renderUI({
tabPanel("Comparison",
{
fluidPage(
fluidRow(
box(
width=12,
solidHeader=T,
title="Parameters",
status="primary",
uiOutput('date_range_UI')
)
)
)
}
)})
output$date_range_UI <-renderUI({
dateRangeInput(
"date_1",
"Period 1",
start=NULL,
end=NULL
)
})
}
ui.R:
uiHeader <- dashboardHeader(title = NULL)
uiSidebar <- dashboardSidebar(sidebarMenuOutput('sidebar_UI'))
uiBody <- dashboardBody(
tags$head(
tags$style(type="text/css"
),
tags$link(rel = "stylesheet", type = "text/css", href = "style_v2.css")
),
uiOutput('body_UI')
)
dashboardPage(uiHeader,
uiSidebar,
uiBody,
skin = "black")
I think that my problem comes from the file style_v2.css :
.progress-bar, .irs-bar,.irs-bar-edge, .irs-from, .irs-to, .irs-single{
background-color:#000033;
}
.box.box-solid.box-primary>.box-header
{
background-color:#000033;
}
.box.box-solid.box-primary{
border: 1px solid #000033;
}
.box.box-primary, .nav-tabs-custom>.nav-tabs>li.active
{
border-top-color:#000033;
}
body {
background-color: #fff;
}
.content-wrapper, .right-side{
background-color:#FFFFFF;
}
.dropdown-menu{
background-color:#333;
}
This file is located in the folder 'www' which is in the same directory than server.R and ui.R.
If I delete this file, then I have no problem. But I need it and I don't know which part is causing this.
I'm trying to insert a period field using dateRangeInput (Shiny).
But I'm having problems in the display.
NB: I encounter the same problem using dateInput.
Here is an extract of server.R:
dateRangeInput(
"date_1",
"Period 1",
start=min(data$Date_processed),
end=""
)
This is completely illegible...
The issue is with this piece of code in your style_v2.css file:
.dropdown-menu {
background-color: #333;
}
#333 is the dark background you are seeing. You can either remove this or edit the hex colour to be lighter.
If that code is necessary for other dropdowns in your code you can be more specific regarding your css - let me know.
I am trying to display an image on the left or right corner and the title in the center on same height. I tried the following code, however, I get the image and title on two different heights. I want to display both side by side.
server.r
shinyServer(function(input, output, session){
})
ui.r
shinyUI(fluidPage(
titlePanel(
headerPanel( title=div(img(src="bigorb.png", height = 100, width = 100),
h3("Image Display Test", align="center", style="bold")
))
)
))
and it displays
You can try to do something like this:
library(shiny)
ui <- shinyUI(fluidPage(shinyjs::useShinyjs(),
tags$link(rel = "stylesheet", type = "text/css", href = "custom-div.css"),
h3(
div(style="display:inline-block;",img(src="bigorb.png", height = 150, width = 150,style="left;")),
div(id="smile","Image Display Test")
),
br(),
sidebarLayout(
sidebarPanel(
textInput("length",
"Enter your length:"),
textInput("weight",
"Enter your weigth:")
),
mainPanel(
htmlOutput("testHTML")
)
)
))
and the .css file. You can find all info that you need about .css file in shiny R here.
#smile {
position: absolute;
width: 300px;
height: 150px;
left: 50%;
margin: -100px 0 0 -150px;
}
I hope it works for you all and keep coding!
PS: I just followed this post