I wish to change the color of text in a Shiny app based on a user's input. Here's a simple example. Is this basically the correct approach? If I hard code the css it works. For example, if I change:
div(style = css_stub,
to
div(style = "inline-block; red;",
the text color changes. Please explain how to alter css in a Shiny app programmatically.
library(shiny)
css_stub <- paste0("'", "inline-block; color:black;", "'")
ui <- fluidPage(
titlePanel("Color Test"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "colors",
label = "Choose a color:",
choices = c("red", "blue"))
),
mainPanel(
div(style = css_stub,
textOutput("text_out"))
)
)
)
server <- function(input, output) {
observeEvent(input$colors, {
if (input$colors == "red") {
css_stub <- paste0("'", "inline-block; color:red;", "'")
output$text_out <- renderText({"hello - red"})
} else {
css_stub <- paste0("'", "inline-block; color:blue;", "'")
output$text_out <- renderText({"hello - blue"})
}
})
}
shinyApp(ui = ui, server = server)
I would define classes and styles for each, then and add/remove classes using shinyjs library.
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
tags$head(
tags$style(HTML("
div.red { color: red; }
div.blue { color: blue; }
"))
),
titlePanel("Color Test"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "colors",
label = "Choose a color:",
choices = c("red", "blue"))
),
mainPanel(
div(id="color_change", style = "inline-block; ",
textOutput("text_out"))
)
)
)
server <- function(input, output) {
observeEvent(input$colors, {
color_to_set <- input$colors
color_to_unset <- setdiff(c("red", "blue"), color_to_set)
shinyjs::addClass("color_change", color_to_set)
for (col in color_to_unset) shinyjs::removeClass("color_change", col)
})
output$text_out = renderText(paste("Hello -", input$colors))
}
shinyApp(ui = ui, server = server)
Related
Any tips to solve encoding problem. I am not able to generate the up (↑) and down (↓) arrows in the code below. When running, the following warning message appears:
Warning messages:
1: unable to translate 'Maximize <U+2191>' to native encoding
2: unable to translate 'Minimize <U+2193>' to native encoding
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(
width = 6,
selectInput("maxmin", label = h5("Maximize or Minimize"),
choices = list("Maximize \u2191" = 1, "Minimize \u2193" = 2), selected = "")
)
)),
mainPanel(
))
)
server <- function(input, output, session) {
}
shinyApp(ui = ui, server = server)
You can use HTML code for the arrows and proceed like this:
library(shiny)
choicesNames <- list("Minimize", "Maximize")
choicesHTML <- list("Minimize ↓", "Maximize ↑")
choices <- setNames(choicesNames, choicesHTML)
ui <- fluidPage(
selectizeInput("select", label = "Select", choices = NULL),
textOutput("txt")
)
server <- function(input, output, session) {
updateSelectizeInput(
session, "select",
choices = choices,
options = list(render = I("
{
item: function(item, escape) { return '<div>' + item.label + '</div>'; },
option: function(item, escape) { return '<div>' + item.label + '</div>'; }
}
"))
)
output$txt <- renderText({
paste("You chose", input$select)
})
}
shinyApp(ui, server)
Another option is to use my package shinySelect and fontawesome icons for the arrows.
library(shiny)
library(shinySelect)
library(bslib)
library(fontawesome)
choices <- HTMLchoices(
labels = list(
tags$span("Minimize", fa_i("arrow-alt-circle-down")),
tags$span("Maximize", fa_i("arrow-alt-circle-up"))
),
values = list("minimize", "maximize")
)
styles <- list(
borderBottom = "5px solid orange",
color = list(selected = "lime", otherwise = "pink"),
backgroundColor = list(selected = "cyan", otherwise = "seashell")
)
ui <- fluidPage(
theme = bs_theme(version = 4),
titlePanel("shinySelect example"),
selectControlInput(
"inputid",
label = tags$h1("Make a choice", style = "color: red;"),
optionsStyles = styles,
choices = choices,
selected = "minimize",
multiple = FALSE,
animated = TRUE
),
br(),
verbatimTextOutput("textOutput")
)
server <- function(input, output, session) {
output$textOutput <- renderPrint({
sprintf("You selected: %s", input$inputid)
})
}
shinyApp(ui, server)
This is an alternate solution. Solutions provided by #Stephane Laurent are great. To translate unicode points to UTF-8, you can use chr_unserialise_unicode() from rlang package. Try this
library(shiny)
library(rlang)
ll <- chr_unserialise_unicode("<U+2193>")
uu <- chr_unserialise_unicode("<U+2191>")
choicesNames <- list(1,2)
choiceValues <- list(sprintf("Minimize %s",ll),sprintf("Maximize %s",uu))
choices <- setNames(choicesNames, choiceValues)
ui <- fluidPage(
selectInput("maxmin", label = h5("Maximize or Minimize"), choices = NULL),
textOutput("mytxt")
)
server <- function(input, output, session) {
updateSelectInput(session, "maxmin", choices = choices )
output$mytxt <- renderText({
paste("You chose", input$maxmin)
})
}
shinyApp(ui = ui, server = server)
When running the code below, you will notice that I have two options below. If you press the Excel option, a fileInput will appear right below the radioButtons. However, I would like to know if it is possible to separate fileInput from radioButtons. I will insert an image to clarify what I want. See that they are separated.
Executable code below:
library(shiny)
library(dplyr)
library(shinyjs)
library(shinythemes)
library(readxl)
ui <- fluidPage(
shiny::navbarPage(theme = shinytheme("flatly"), collapsible = TRUE,
br(),
tabPanel("PAGE1",
sidebarLayout(
sidebarPanel(
radioButtons("button",
label = h3("Data source"),
choices = list("Excel" = "Excel",
"Database" = "database"),
selected = "File"),
uiOutput('fileInput'),
),
mainPanel(
)))))
server <- function(input, output) {
observe({
if(is.null(input$button)) {
}else if (input$button =="Excel"){
output$fileInput <- renderUI({
fileInput("file",h4("Import file"), multiple = T, accept = ".xlsx")
})
} else if(input$button=="database"){
output$fileInput <- NULL
} else {
output$fileInput <- NULL
}
})
}
shinyApp(ui = ui, server = server)
Example:
I left it in red to specify the space
A possible workaround could be to use fluidRow with two columns to simulating a sidebarPanel with a mainPanel.
Notice that I wrapped the inputs in a div(class = "well well-lg") for the background.
App
library(shiny)
library(dplyr)
library(shinyjs)
library(shinythemes)
library(readxl)
ui <- navbarPage(
theme = shinytheme("flatly"), collapsible = TRUE,
br(),
tabPanel(
"PAGE1",
fluidRow(
column(
width = 6,
fluidRow(div(
class = "well well-lg",
radioButtons("button",
label = h3("Data source"),
choices = list(
"Excel" = "Excel",
"Database" = "database"
),
selected = "File"
)
)),
fluidRow(
uiOutput("fileInput")
)
),
column(
width = 6,
tableOutput("iris")
)
)
)
)
server <- function(input, output) {
output$iris <- renderTable({
iris
})
observe({
if (is.null(input$button)) {
} else if (input$button == "Excel") {
output$fileInput <- renderUI({
div(class = "well well-lg", fileInput("file", h4("Import file"), multiple = T, accept = ".xlsx"))
})
} else if (input$button == "database") {
output$fileInput <- NULL
} else {
output$fileInput <- NULL
}
})
}
shinyApp(ui = ui, server = server)
I wonder if it is possible to change the color of text in validate() in a shiny app since there is not an id like for example when you use textOutput().
library(shiny)
ui <- fluidPage(
checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
selectizeInput('in2', 'Select a state', choices = state.name),
plotOutput('plot')
)
server <- function(input, output) {
output$plot <- renderPlot({
validate(
need(input$in1, 'Check at least one letter!'),
need(input$in2 != '', 'Please choose a state.')
)
plot(1:10, main = paste(c(input$in1, input$in2), collapse = ', '))
})
}
shinyApp(ui, server)
You can provide a CSS style as described here: https://shiny.rstudio.com/articles/validation.html
library(shiny)
ui <- fluidPage(
tags$head(
tags$style(HTML("
.shiny-output-error-validation {
color: #ff0000;
font-weight: bold;
}
"))
),
checkboxGroupInput('in1', 'Check some letters', choices = head(LETTERS)),
selectizeInput('in2', 'Select a state', choices = state.name),
plotOutput('plot')
)
server <- function(input, output) {
output$plot <- renderPlot({
validate(
need(input$in1, 'Check at least one letter!'),
need(input$in2 != '', 'Please choose a state.')
)
plot(1:10, main = paste(c(input$in1, input$in2), collapse = ', '))
})
}
shinyApp(ui, server)
Result
I wish to make the first element "1" of the selectInput bold in color. Please help.
ui <- fluidPage(
selectInput(
"select",
label = h3("Select box"),
choices = c(1,2,3,4)
))
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
Have a look at the shinyWidgets package which has a lot of cool features with its pickerInput
rm(list = ls())
library(shiny)
library(shinyWidgets)
ui <- fluidPage(
pickerInput(inputId = "Id069",
label = "Style individual options with HTML",
choices = c("steelblue 150%",
"right align + red", "bold",
"background color"), choicesOpt = list(style = c("color: steelblue; font-size: 150%;",
"color: firebrick; text-align: right;",
"font-weight: bold;", "background: forestgreen; color: white;")))
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
You can add the style as suggested by #Nitin Shinde in your shiny app like this:
ui <- fluidPage(
tags$head(tags$style(".option:first-child{
font-weight:bold;
//color:#ff0000;
}")),
selectInput(
"select",
label = h3("Select box"),
choices = c(1,2,3,4)
))
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
The output would be something like this:
You can use pseudo elements in CSS
<style>
option:first-child{
font-weight:bold;
color:#ff0000;
}
</style>
You can use the below and nest each selectInput inside the div with class = "test" for every one you wish the first item to be bold in.
ui <- fluidPage(
tags$head(tags$style(".test .option:first-child{
font-weight:bold;
//color:#ff0000;
}")),
div(class = "test",selectInput(
"select",
label = h3("Select box"),
choices = c(1,2,3,4)
)),
selectInput(
"select2",
label = h3("Select box"),
choices = c(1,2,3,4)
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
You can set the class of the div to whatever you like just be sure to change the .test part of the CSS accordingly.
Updating "//color:#ff0000;" to "color:#ff0000;" will change the colour to red, just update the hex code to whichever colour you would like to use.
I am developing a Shiny app with a plot (plot1 in the code) that is reactive to a data table (rhandsontable) and it displays the item selected on the table.
The table is very large so you have to scroll down to see everything. But I want the plot to be always visible, so to be fixed in the layout while you scroll down the table.
There is anyway to do it? I have done a lot of research but any answer that can help me.
My UI code is that:
ui <- dashboardPage(
dashboardHeader(title = "IG Suppliers: Tim"),
dashboardSidebar(
sidebarMenu(
menuItem("Data Cleansing", tabName = "DataCleansing", icon = icon("dashboard")),
selectInput("supplier","Supplier:", choices = unique(dt_revision_tool$Supplier)),
#selectInput("supplier","Supplier:", choices = 'Phillips'),
selectInput("segment","Segment:", choices = unique(dt_revision_tool$Segment_Name), multiple = TRUE, selected = unique(dt_revision_tool$Segment_Name)[1]),
#selectInput("segment","Segment:", choices = sgm),
selectInput("alert","Alert", choices = unique(dt_revision_tool$Alert),selected = "Yes"),
#selectInput("alert","Alert", choices = c('Yes','No'),selected = "Yes"),
selectInput("dfu","DFU", choices = c("NULL",unique(dt_revision_tool$DFU)),selected = "NULL"),
tags$hr()
# h5("Save table",align="center"),
#
# div(class="col-sm-6",style="display:inline-block",
# actionButton("save", "Save"),style="float:center")
)
),
dashboardBody(
shinyjs::useShinyjs(),
#First Tab
tabItems(
tabItem(tabName= "DataCleansing",
fluidPage(theme="bootstrap.css",
fluidRow(
plotOutput('plot1')
),
fluidRow(
verbatimTextOutput('selected'),
rHandsontableOutput("hot")
)
)
)
# #Second Tab
# tabItem(tabName = "Forecast",
# h2('TBA')
# )
)
)
)
The server code is that:
server <- shinyServer(function(input, output) {
if (file.exists("DF.RData")==TRUE){
load("DF.RData")
}else{
load("DF1.RData")
}
rv <- reactiveValues(x=dt_revision_tool)
dt <- reactiveValues(y = DF)
observe({
output$hot <- renderRHandsontable({
view = data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu))
if (nrow(view)>0){
rhandsontable(view,
readOnly = FALSE, selectCallback = TRUE, contextMenu = FALSE) %>%
hot_col(c(1:12,14),type="autocomplete", readOnly = TRUE)
}
})
})
observe({
if (!is.null(input$hot)) {
aux = hot_to_r(input$hot)
aux = subset(aux, !is.na(Cleansing_Suggestion) | Accept_Cleansing,select=c('DFU','Week','Cleansing_Suggestion',
'Accept_Cleansing'))
names(aux) = c('DFU','Week','Cleansing_Suggestion_new','Accept_Cleansing_new')
dt$y = update_validations(dt$y,aux)
DF = dt$y
save(DF, file = 'DF.RData')
}
})
output$plot1 <- renderPlot({
view = data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu))
if (nrow(view)>0){
if (!is.null(( data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$DFU)) {
s = make_plot2(rv$x,(data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$DFU,(data.table(update_view(rv$x,input$alert,input$segment,input$supplier,dt$y,input$dfu)))[input$hot_select$select$r]$Article_Name)
print(s)
}
}
})
})
Any help or idea will be welcome!
Thanks!
Aida
Here is an example of using CSS position: fixed to do this. You can adjust the position top and margin-top according to your requirement.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
tags$div(p("Example of fixed plot position"))
),
mainPanel(
plotOutput("plot"),
tableOutput("table"),
tags$head(tags$style(HTML("
#plot {
position: fixed;
top: 0px;
}
#table {
margin-top: 400px;
}
")))
)
)
))
server <- shinyServer(function(input, output, session) {
output$plot <- renderPlot({
plot(iris$Sepal.Length, iris$Sepal.Width)
})
output$table <- renderTable({
iris
})
})
shinyApp(ui = ui, server = server)