I made a simple example to show my problem, which is:
how to set notifications at different places independently of each others (let's say I want each notification to be displayed at the top right and left, respectively)
```
library(shiny)
ui <- fluidPage(
# tags$head(
# tags$style(
# HTML(".shiny-notification {
# position: fixed;
# top: 800px;
# left: 40px;
# width: 15em;
# opacity: 1;
# }
# "
# )
# )
# )
actionButton("showme", "Show Notification:")
)
server <- function(input, output, session) {
observe({
showNotification(
id = "welcome_notif",
"Blablablablabla .... blablablabla.",
duration = 20,
closeButton = TRUE,
type = "message")
})
observeEvent(input$showme,{
showNotification(
id = "showme_notif",
"Hihihi", # put text in notification
duration = 30,
closeButton = TRUE,
type = "message")
})
}
shinyApp(ui = ui, server = server)
```
I saw that there is a special CSS for notification in shiny code (https://github.com/rstudio/shiny/blob/master/inst/www/shared/shiny.css).
If I change the CSS class (as shown in commented code), I only manage to change the place where all notifications will be displayed (but not independently).
EDIT:
I tried to use addClass with shinyjs:
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
inlineCSS(list(.customclass = "position: fixed; top: 200px;")),
actionButton("showme", "Show Notification:")
)
server <- function(input, output, session) {
observe({
showNotification(
id = "welcome_notif",
"Blablablablabla .... blablablabla.",
duration = 20,
closeButton = TRUE,
type = "message")
})
observeEvent(input$showme,{
showNotification(
id = "showme_notif",
"Hihihi", # put text in notification
duration = 30,
closeButton = TRUE,
type = "message")
})
observe({
addClass(id = "showme_notif", class = "customclass")
})
}
shinyApp(ui = ui, server = server)
as suggested by Florian (see below) but it seems that I can only handle elements generated in UI and not in server like notifications.
For example this works:
if (interactive()) {
shinyApp(
ui = fluidPage(
useShinyjs(),
inlineCSS(list(.customclass = "position: fixed; top: 200px;")),
p(id = "element", "Watch what happens to me")
),
server = function(input, output) {
observe({
addClass(id = "element", class = "customclass")
})
}
)
}
I am able to modify the CSS of the element, since the notification gets the id: shiny-notifaction-xxx where xxx is the name of your notification. But all notifications are put together in another container, and I am unable to modify the CSS so that it does what you want.
library(shiny)
ui <- fluidPage(
tags$style("#shiny-notification-showme_notif {margin:20px;}"),
actionButton("showme", "Show Notification:")
)
server <- function(input, output, session) {
observe({
showNotification(
id = "welcome_notif",
"Blablablablabla .... blablablabla.",
duration = 200,
closeButton = TRUE,
type = "message")
})
observeEvent(input$showme,{
showNotification(
id = "showme_notif",
"Hihihi", # put text in notification
duration = 300,
closeButton = TRUE,
type = "message")
})
}
shinyApp(ui = ui, server = server)
So according to Florian, one answer could be:
library(shiny)
ui <- fluidPage(
tags$style("#shiny-notification-showme_notif {position: fixed; top: 800px; left: 40px; width: 15em; opacity: 1;}"),
tags$style("#shiny-notification-welcome_notif {position: fixed; top: 800px; right: 40px; width: 15em; opacity: 1;}"),
actionButton("showme", "Show Notification:")
)
server <- function(input, output, session) {
observe({
showNotification(
id = "welcome_notif",
"Blablablablabla .... blablablabla.",
duration = 200,
closeButton = TRUE,
type = "message")
})
observeEvent(input$showme,{
showNotification(
id = "showme_notif",
"Hihihi", # put text in notification
duration = 300,
closeButton = TRUE,
type = "message")
})
}
shinyApp(ui = ui, server = server)
and can be modified, as required.
Related
I'm trying to make the font size of the words on each button to be smaller (which would hopefully also shrink the overall button size for each option) but am struggling to find the right place to write the style = 'font-size: 5px' code. Where would I add that?
library(shiny)
library(shinyjqui)
server <- function(input, output) {
output$order <- renderPrint({ print(input$dest) })
}
ui <- fluidPage(
orderInput('source', 'Source', items = month.abb,
as_source = TRUE, connect = 'dest'),
orderInput('dest', 'Dest', items = NULL, placeholder = 'Drag items here...'),
verbatimTextOutput('order')
)
shinyApp(ui, server)
library(shiny)
library(shinyjqui)
server <- function(input, output) {
output$order <- renderPrint({ print(input$dest) })
}
ui <- fluidPage(
orderInput('source', 'Source', items = month.abb,
as_source = TRUE, connect = 'dest'),
orderInput('dest', 'Dest', items = NULL, placeholder = 'Drag items here...'),
verbatimTextOutput('order'),
tags$style(HTML(
'
.btn.shinyjqui {font-size: 5px}
'
))
)
shinyApp(ui, server)
to also shrink the button
tags$style(HTML(
'
.btn.shinyjqui {
font-size: 5px;
padding: 0;
}
'
))
I can successfully move the "next" button for slickR's carousel. However, when I use the similar method to move the "previous" button it does not work. The action and the mouseover no longer work. Why is this? How can I move the "prev" button and maintain full functionality?
The documentation refers to an element in settings called, appendArrows. But it is not clear to me how to use this.
appendArrows character, Change where the navigation arrows are attached (Selector, htmlString, Array, Element, jQuery object), Default: $(element)
Here is where the fully functional moved buttons should appear:
library(shiny)
library(slickR)
# Test #########################################################################
chart_names_list <- c( "http://placehold.it/900x500/39CCCC/ffffff&text=Slide+1",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+2",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+3")
num_slides <- 2
ui <- fluidPage(
tags$head(
tags$style(HTML("
.slick-next {
right: 163px;
top: 20px;
}
.slick-prev {
left: 670px;
top: 20px;
}
.slick-slide {
margin-top: 30px;
}
")
)
),
slickROutput("slick_output")
)
server <- function(input, output, session) {
output$slick_output <- renderSlickR({
slickR(obj = chart_names_list, height = 300, width = "100%") +
settings(dots = TRUE)
})
}
shinyApp(ui, server)
appendArrows parameter is used to tell in which div class the arrows should be displayed.
This shows the principle, but still needs some extra css to get exactly the result you expect :
library(shiny)
library(slickR)
# Test #########################################################################
chart_names_list <- c( "http://placehold.it/900x500/39CCCC/ffffff&text=Slide+1",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+2",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+3")
num_slides <- 2
ui <- fluidPage(
tags$head(
tags$style(HTML("
.arrows {
height: 30px;}"))
),
fluidRow(
column(6,),
column(2,
tags$div(class="arrows"
)),column(4)),
slickROutput("slick_output")
)
server <- function(input, output, session) {
output$slick_output <- renderSlickR({
slickR(obj = chart_names_list, height = 300, width = "100%") +
settings(dots = TRUE, appendArrows = '.arrows')
})
}
shinyApp(ui, server)
Taking #Waldi's valuable suggestions and adding some css leads to a complete answer below.
library("shiny")
library("slickR")
# Test #########################################################################
chart_names_list <- c( "http://placehold.it/900x500/39CCCC/ffffff&text=Slide+1",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+2",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+3")
num_slides <- 3
ui <- fluidPage(
tags$head(
tags$style(HTML("
.arrows {
height: 30px;
}
.slick-prev {
left: 230px; # moves right
}
.slick-next {
left: 250px; # moves right
}
"))
),
fluidRow(
column(6,),
column(2,
tags$div(class="arrows"
)),
column(4)),
slickROutput("slick_output")
)
server <- function(input, output, session) {
output$slick_output <- renderSlickR({
slickR(obj = chart_names_list, height = 300, width = "100%") +
settings(dots = TRUE, appendArrows = '.arrows')
})
}
shinyApp(ui, server)
As this is the original question regarding the positioning of the arrow buttons, I guess it's worth mentioning, that #ixodid realized here, that #Waldi's column-approach is no longer working when the browser window is resized.
The following is a workaround regarding this:
library("shiny")
library("slickR")
chart_names_list <- c( "http://placehold.it/900x500/39CCCC/ffffff&text=Slide+1",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+2",
"http://placehold.it/900x500/39CCCC/ffffff&text=Slide+3")
num_slides <- 3
ui <- fluidPage(
tags$head(
tags$style(HTML("
.arrows {
height: 20px;
}
.slick-prev {
left: calc(100% - 60px);
}
.slick-next {
left: calc(100% - 35px);
}
.slick-slide img {
width: 100% !important;
}
"))
),
fluidRow(
column(12, tags$div(class="arrows"))
),
slickROutput("slick_output")
)
server <- function(input, output, session) {
output$slick_output <- renderSlickR({
slickR(obj = chart_names_list, height = 300) +
settings(dots = TRUE, appendArrows = '.arrows')
})
}
shinyApp(ui, server)
Below is a shiny app which displays a slideshow of images with the slickR package. How to get the name of the current image?
library(shiny)
library(slickR)
ui <- fluidPage(
tags$div(
slickROutput("slickr", width="500px"),
style = "margin-left:100px;"
)
)
server <- function(input, output) {
imgs <- list.files("~/", pattern=".png", full.names = TRUE)
output[["slickr"]] <- renderSlickR({
slickR(imgs)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Here is a solution with a MutationObserver:
library(shiny)
library(slickR)
js <- "
$(document).ready(function(){
var ss = document.getElementById('slickr');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
var index = $(ss).find('.slick-current').data('slick-index');
Shiny.setInputValue('imageIndex', parseInt(index)+1);
});
// configuration of the observer
var config = {subtree: true, attributes: true};
// observe
observer.observe(ss, config);
})
"
ui <- fluidPage(
tags$head(
tags$script(HTML(js))
),
textOutput("imgName"),
tags$hr(),
tags$div(
slickROutput("slickr", width="500px"),
style = "margin-left:100px;"
)
)
server <- function(input, output) {
imgs <- list.files("~/", pattern=".png", full.names = TRUE)
output[["slickr"]] <- renderSlickR({
slickR(imgs)
})
output[["imgName"]] <- renderText({
paste0("CURRENT IMAGE: ", basename(imgs[input[["imageIndex"]]]))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Another solution, simpler: replace js with
js <- "
$(document).ready(function(){
$('#slickr').on('setPosition', function(event, slick) {
var index = slick.currentSlide + 1;
Shiny.setInputValue('imageIndex', index);
});
})"
Maybe something like this workaround?
I am using the index of the image and get the basename of the imagelist.
library(shiny)
library(slickR)
jscode <- HTML("
$(document).on('shiny:connected', function(event) {
var imagindex = 0;
Shiny.onInputChange('slickin', imagindex);
$(document).on('click', '.slick-arrow', function(event) {
var imagindex = $('.slick-active')[0].attributes[1].value;
Shiny.onInputChange('slickin', imagindex);
});
$(document).on('click', '.slick-dots', function(event) {
var imagindex = $('.slick-active')[0].attributes[1].value;
Shiny.onInputChange('slickin', imagindex);
});
});
")
ui <- fluidPage(
tags$head(tags$script(jscode)),
tags$div(
slickROutput("slickr", width="500px"),
style = "margin-left:100px;"
)
)
server <- function(input, output) {
imgs <- list.files(getwd(), pattern=".png", full.names = TRUE);
output[["slickr"]] <- renderSlickR({
slickR(imgs)
})
observe( {
req(input$slickin)
print(basename(imgs[as.numeric(input$slickin) + 1]))
})
}
shinyApp(ui = ui, server = server)
The slickR shiny vignette describes the "official" way without using custom JS:
Observe the active slick
The htmlwidget is observed by shiny and information can be retrieved.
Using the output name you set for the renderSlick object in this example
it is output$slick_output
Using this you can interact server-side "on click" of the active carousel
by accessing elements in input$slick_output_current$
.clicked : The index of the clicked element
.relative_clicked: The relative position of the clicked element
.center : The index of the center element
.total : The total number of elements in the carousel
.active : The ID of the active carousel
library(shiny)
library(slickR)
# create some local images
if(!dir.exists("myimages")){
dir.create("myimages")
}
imgs <- paste0("myimages/myplot", seq_len(3), ".png")
for (myPlot in myPlots) {
png(file = myPlot, bg = "transparent")
plot(runif(10))
dev.off()
}
ui <- fluidPage(
tags$head(
tags$script(HTML(js))
),
textOutput("imgName"),
tags$hr(),
tags$div(
slickROutput("slickr", width="500px"),
style = "margin-left:100px;"
)
)
server <- function(input, output) {
output[["slickr"]] <- renderSlickR({
slickR(imgs)
})
output[["imgName"]] <- renderText({
paste0("CURRENT IMAGE: ", basename(imgs[input$slickr_current$.center]))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Here's a solution from one of the slickR vignettes:
slickR(obj = nba_player_logo$uri[1:2], height = 100, width = "95%") %synch%
( slickR(nba_player_logo$name[1:2], slideType = 'p') + settings(arrows = FALSE) )
Worked great for me.
I am working on a shiny app that takes a long time to do calculations, I want to have a modal progress bar that closes automatically as soon as all calculations work.
The ideal solution would have two features
Covers most of the screen and prevents the user to interact with app
Closes automatically as soon as it finishes making calulations
I found this solution in the following question:
library("shiny")
library("shinyWidgets")
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch long calculation"), #, onclick = "$('#my-modal').modal().focus();"
# You can open the modal server-side, you have to put this in the ui :
tags$script("Shiny.addCustomMessageHandler('launch-modal', function(d) {$('#' + d).modal().focus();})"),
tags$script("Shiny.addCustomMessageHandler('remove-modal', function(d) {$('#' + d).modal('hide');})"),
# Code for creating a modal
tags$div(
id = "my-modal",
class="modal fade", tabindex="-1", `data-backdrop`="static", `data-keyboard`="false",
tags$div(
class="modal-dialog",
tags$div(
class = "modal-content",
tags$div(class="modal-header", tags$h4(class="modal-title", "Calculation in progress")),
tags$div(
class="modal-body",
shinyWidgets::progressBar(id = "pb", value = 0, display_pct = TRUE)
),
tags$div(class="modal-footer", tags$button(type="button", class="btn btn-default", `data-dismiss`="modal", "Dismiss"))
)
)
)
)
server <- function(input, output, session) {
value <- reactiveVal(0)
observeEvent(input$go, {
shinyWidgets::updateProgressBar(session = session, id = "pb", value = 0) # reinitialize to 0 if you run the calculation several times
session$sendCustomMessage(type = 'launch-modal', "my-modal") # launch the modal
# run calculation
for (i in 1:10) {
Sys.sleep(0.5)
newValue <- value() + 1
value(newValue)
shinyWidgets::updateProgressBar(session = session, id = "pb", value = 100/10*i)
}
Sys.sleep(0.5)
# session$sendCustomMessage(type = 'remove-modal', "my-modal") # hide the modal programmatically
})
}
shinyApp(ui = ui, server = server)
This solves issue 1, but I have to click on dismiss to see the results
The original progressbar provided in shiny is exactly what you need.
But I use css to make the progessbar display in the middle in the screen.
You can find the detail of using progress bar in shiny here.
library("shiny")
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch long calculation"), #, onclick = "$('#my-modal').modal().focus();"
# css to center the progress bar
tags$head(
tags$style(
HTML(".shiny-notification {
height: 100px;
width: 800px;
position:fixed;
top: calc(50% - 50px);
left: calc(50% - 400px);
font-size: 250%;
text-align: center;
}
"
)
)
)
)
server <- function(input, output, session) {
value <- reactiveVal(0)
observeEvent(input$go, {
withProgress(message = 'Calculation in progress', value = 0,detail="0%", {
# run calculation
for (i in 1:10) {
Sys.sleep(0.5)
newValue <- value() + 1
value(newValue)
incProgress(1/10,detail = paste0(i*10,"%"))
}
Sys.sleep(0.5)
})
})
}
shinyApp(ui = ui, server = server)
Not a whole answer, just answering the additional css requests. You could change the css to, which will make the panel fill up the whole page.
.shiny-notification {
height: 100%;
width: 100%;
top: 0;
left: 0;
position:fixed;
font-size: 250%;
text-align: center;
background-color: rgba(0, 0, 0, 0.7);
color: white;
}
I'm working with a Shiny app where I need to calculate processes and while the calc progress is executing, I'm using a progressBar to show the process.
The problem is that the progress bar is too small, and I don't like the way is shown.
So, I was thinking that maybe there's a way to implement a progress bar using a Shiny modal (there's a function called modalDialog).
My idea is that when the user runs the calc, a modal will be opened showing a progressBar.
This is the progress code:
withProgress(message = 'Runing GSVA', value = 0, {
incProgress(1, detail = "This may take a while...")
functionToGenerate()
})
Any idea?
I would suggest customizing the CSS class of the notification:
If you inspect the element of the notifier you see that it has the class "shiny-notification". So you can overwrite some properties of that class with tags$style(). In the example below (for the template: see ?withProgress) i decided to adjust height+width to make it bigger and top+left to center it.
ui <- fluidPage(
tags$head(
tags$style(
HTML(".shiny-notification {
height: 100px;
width: 800px;
position:fixed;
top: calc(50% - 50px);;
left: calc(50% - 400px);;
}
"
)
)
),
plotOutput("plot")
)
server <- function(input, output) {
output$plot <- renderPlot({
withProgress(message = 'Calculation in progress',
detail = 'This may take a while...', value = 0, {
for (i in 1:15) {
incProgress(1/15)
Sys.sleep(0.25)
}
})
plot(cars)
})
}
runApp(shinyApp(ui, server), launch.browser = TRUE)
I wrote a progress bar function in the package shinyWidgets, you can put it in a modal, but it's tricky to use with shiny::showModal, so you can create your own modal manually like the below. It's more code to write but it works fine.
library("shiny")
library("shinyWidgets")
ui <- fluidPage(
actionButton(inputId = "go", label = "Launch long calculation"), #, onclick = "$('#my-modal').modal().focus();"
# You can open the modal server-side, you have to put this in the ui :
tags$script("Shiny.addCustomMessageHandler('launch-modal', function(d) {$('#' + d).modal().focus();})"),
tags$script("Shiny.addCustomMessageHandler('remove-modal', function(d) {$('#' + d).modal('hide');})"),
# Code for creating a modal
tags$div(
id = "my-modal",
class="modal fade", tabindex="-1", `data-backdrop`="static", `data-keyboard`="false",
tags$div(
class="modal-dialog",
tags$div(
class = "modal-content",
tags$div(class="modal-header", tags$h4(class="modal-title", "Calculation in progress")),
tags$div(
class="modal-body",
shinyWidgets::progressBar(id = "pb", value = 0, display_pct = TRUE)
),
tags$div(class="modal-footer", tags$button(type="button", class="btn btn-default", `data-dismiss`="modal", "Dismiss"))
)
)
)
)
server <- function(input, output, session) {
value <- reactiveVal(0)
observeEvent(input$go, {
shinyWidgets::updateProgressBar(session = session, id = "pb", value = 0) # reinitialize to 0 if you run the calculation several times
session$sendCustomMessage(type = 'launch-modal', "my-modal") # launch the modal
# run calculation
for (i in 1:10) {
Sys.sleep(0.5)
newValue <- value() + 1
value(newValue)
shinyWidgets::updateProgressBar(session = session, id = "pb", value = 100/10*i)
}
Sys.sleep(0.5)
# session$sendCustomMessage(type = 'remove-modal', "my-modal") # hide the modal programmatically
})
}
shinyApp(ui = ui, server = server)