Related
Currently, I am making a dashboard with rshiny where I plot a graph. However, I am wondering if there is a way to add a tooltip next to the X and Y axis titles in the plot?
Specifically, a question mark or (something equivalent) next to the axis title, where end-users can click or hover on to see a more extensive description of what the axis really mean.
Something like this (does not work though):
layout(showlegend = FALSE,
separators = ',.',
xaxis = list(title = "Age", tooltip = "The age of every female above 50 years in the Unites States of America"))
Unfortunately I don't know a package that can do this but you can use plotly with HTML and JS to bind a container to your title that contains a tooltip:
Edit: added yaxis (which tends to be a bit tougher)
df <- data.frame(a = 1:3,
b = 4:6)
jscode <- "
$(document).ready(function() {
$(\"[data-toggle='tooltip']\").tooltip({container: 'body'});
});
"
csscode <- HTML('
.plot-container {
position: relative;
}
.xaxis-container {
height: 20px;
position:absolute;
bottom: 0;
left: 40px;
background: #fff;
opacity: 0.5;
}
.yaxis-container {
width: 20px;
position:absolute;
bottom: 0;
left: 5px;
background: #fff;
opacity: 0.5;
}
.xaxis-tooltip {
width: 30px;
height: 20px;
background: #000;
margin:auto;
}
.yaxis-tooltip {
width: 20px;
height: 30px;
background: #000;
margin:auto;
}
')
library(shiny)
library(plotly)
ui <- fluidPage(
tags$head(
tags$script(jscode),
tags$style(csscode)
),
div(class = 'plot-container',
plotlyOutput("plot"),
div(
class = "xaxis-container",
div(class = "xaxis-tooltip", "data-toggle" = "tooltip", "title" = "x")
),
div(
class = "yaxis-container",
div(class = "yaxis-tooltip", "data-toggle" = "tooltip", "title" = "y")
)
)
)
server = function(input, output) {
output$plot <- renderPlotly({
plot_ly() %>%
add_trace(
data = df,
x = ~ a,
y = ~ b,
type = "scatter"
) %>%
htmlwidgets::onRender("
function(el, x) {
var width = $('.draglayer')[0].getBoundingClientRect().width;
var height = 0.5*$('.yaxis-tooltip')[0].getBoundingClientRect().height+$('.plot-container')[0].getBoundingClientRect().height-0.5*$('.draglayer')[0].getBoundingClientRect().height;
$('.xaxis-container').css('width', width);
$('.yaxis-container').css('height', height);
}
")
})
}
shinyApp(ui, server)
Which will look like so:
You can change the opacity to 0 to make the containers invisible, this is just a proof of concept.
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)
I have a shiny app where a datatable is displayed. There is a column with a checkbox that allows the user to select the row and when pressing a button a modal is displayed. The modal contains a table with a subset of the datatable including only the row selected (my real app triggers another function but the effect is the same)
However, when the user deselects the row and selects another row, the previous content in the model is displayed before being replaced with the new one.
Is there any way of resetting the model everytime the button is pressed?
Here is the code I am using:
library(shinydashboard)
library(shinydashboardPlus)
library(shiny)
library(flextable)
data(mtcars)
header <- dashboardHeader()
sidebar <- dashboardSidebar()
body <- dashboardBody(
fluidPage(
tags$head(tags$style("#modal1 .modal-body {padding: 10px}
#modal1 .modal-content {-webkit-border-radius: 12px !important;-moz-border-radius: 12px !important;border-radius: 12px !important;}
#modal1 .modal-dialog { width: 800px; display: inline-block; text-align: left; vertical-align: top;}
#modal1 .modal-header {background-color: #339FFF; border-top-left-radius: 6px; border-top-right-radius: 6px}
#modal1 .modal { text-align: center; padding-right:10px; padding-top: 24px;}
#moda1 .close { font-size: 16px}")),
tags$script(HTML('$(".modal").on("hidden.modal1", function(){
$(this).removeData();
});'
)
),
fluidRow(
column(2,offset = 2,
HTML('<div class="btn-group" role="group" aria-label="Basic example">'),
actionButton(inputId = "Compare_row_head",label = "Get full data"),
HTML('</div>')
),
column(12,dataTableOutput("tabla")),
tags$script(HTML('$(document).on("click", "input", function () {
var checkboxes = document.getElementsByName("row_selected");
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].value);
}
}
Shiny.onInputChange("checked_rows",checkboxesChecked);})')
),
tags$script("$(document).on('click', '#Main_table button', function () {
Shiny.onInputChange('lastClickId',this.id);
Shiny.onInputChange('lastClick', Math.random())
});")
)
)
)
ui <- dashboardPagePlus(enable_preloader = TRUE, sidebar_fullCollapse = TRUE, header, sidebar, body)
## Server side
server = function(input, output, session) {
data("mtcars")
# Reactive function creating the DT output object
output$tabla <- renderDataTable({
req(mtcars)
data <- mtcars
data
data[["Select"]]<-paste0('<input type="checkbox" name="row_selected" value="Row',1:nrow(data),'"><br>')
datatable(data, escape = FALSE)
})
###Modal visualisation
observeEvent(input$Compare_row_head,{
showModal(tags$div(id="modal1", annotation_modal1))
}
)
annotation_modal1<-modalDialog(
fluidPage(
h3(strong("Example modal"),align="left"),
uiOutput('disTable')
),
size="l"
)
output$disTable <- renderUI({
req(input$checked_rows)
row_to_sel=as.numeric(gsub("Row","",input$checked_rows))
if (length(row_to_sel)){
#if (length(s)) {
#df <- vals$fake_sales
df <- mtcars
df <- as.data.frame(df[row_to_sel,])
ft <- flextable(df)
ft <- flextable::bold(ft, part="header")
ft <- flextable::autofit(ft)
ft <- flextable::width(ft, j=2, width=.1)
ft <- flextable::align(ft, align = "left", part = "all" )
ft %>% htmltools_value()
}
})
} # Server R
shinyApp(ui, server)
In the code pasted above I have tried to reset the modal using this:
tags$script(HTML('$(".modal").on("hidden.modal1", function(){
$(this).removeData();
});'
)
)
But it doesn't work
Thanks
The problem here is, that disTable only is rendered when your modalDialog is triggered (not already when the boxes are checked).
We can force shiny to render disTable earlier (when input$checked_rows is changed) by setting:
outputOptions(output, "disTable", suspendWhenHidden = FALSE)
Please check the following:
library(shinydashboard)
library(shinydashboardPlus)
library(shiny)
library(DT)
library(flextable)
data(mtcars)
header <- dashboardHeader()
sidebar <- dashboardSidebar()
body <- dashboardBody(
fluidPage(
tags$head(tags$style("#modal1 .modal-body {padding: 10px}
#modal1 .modal-content {-webkit-border-radius: 12px !important;-moz-border-radius: 12px !important;border-radius: 12px !important;}
#modal1 .modal-dialog { width: 800px; display: inline-block; text-align: left; vertical-align: top;}
#modal1 .modal-header {background-color: #339FFF; border-top-left-radius: 6px; border-top-right-radius: 6px}
#modal1 .modal { text-align: center; padding-right:10px; padding-top: 24px;}
#moda1 .close { font-size: 16px}")),
fluidRow(
column(2,offset = 2,
HTML('<div class="btn-group" role="group" aria-label="Basic example">'),
actionButton(inputId = "Compare_row_head",label = "Get full data"),
HTML('</div>')
),
column(12,dataTableOutput("tabla")),
tags$script(HTML('$(document).on("click", "input", function () {
var checkboxes = document.getElementsByName("row_selected");
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].value);
}
}
Shiny.onInputChange("checked_rows",checkboxesChecked);})')
),
tags$script("$(document).on('click', '#Main_table button', function () {
Shiny.onInputChange('lastClickId',this.id);
Shiny.onInputChange('lastClick', Math.random())
});")
)
)
)
ui <- dashboardPagePlus(enable_preloader = TRUE, sidebar_fullCollapse = TRUE, header, sidebar, body)
## Server side
server = function(input, output, session) {
data("mtcars")
# Reactive function creating the DT output object
output$tabla <- renderDataTable({
req(mtcars)
data <- mtcars
data
data[["Select"]]<-paste0('<input type="checkbox" name="row_selected" value="Row',1:nrow(data),'"><br>')
datatable(data, escape = FALSE)
})
###Modal visualisation
observeEvent(input$Compare_row_head,{
showModal(tags$div(id="modal1", annotation_modal1))
}
)
annotation_modal1 <- modalDialog(
fluidPage(
h3(strong("Example modal"), align="left"),
uiOutput('disTable')
),
size="l"
)
output$disTable <- renderUI({
req(input$checked_rows)
row_to_sel=as.numeric(gsub("Row", "", input$checked_rows))
if (length(row_to_sel)){
#if (length(s)) {
#df <- vals$fake_sales
df <- mtcars
df <- as.data.frame(df[row_to_sel,])
ft <- flextable(df)
ft <- flextable::bold(ft, part="header")
ft <- flextable::autofit(ft)
ft <- flextable::width(ft, j=2, width=.1)
ft <- flextable::align(ft, align = "left", part = "all" )
ft %>% htmltools_value()
}
})
outputOptions(output, "disTable", suspendWhenHidden = FALSE)
} # Server R
shinyApp(ui, server)
The problem I face is that observers linked to dynamically rendered elements seem to fire on render, while this is not how I want it to be.
The reason this is a problem, is that the color buttons I'm making are linked to a plot that takes several seconds to render (plotly widget)
I added ignoreInit = T the observers that are created, but they still fire on rendering, unlike normal observers linked to a button build directly in the UI
How do I stop the observers linked to the dynamically rendered colourInput from firing when the element is rendered?
In the dummy app below the following series of events is recreated in simplified form:
A model spits out a number (simulated by test button in demo app)
Based on this number, a number of colourInput buttons are made
A same number of observeEvents are made for each.
Not in the dummy app: When the user chooses to change a color, the corresponding group in plots is recolored accordingly
The test app contains a working static colourInput, and a dynamic part that demonstrates the problem scenario.
Test app:
library(shiny)
library("colourpicker")
THECOLORS <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400', '#990000',
'#505050', '#a02ca0', '#000099', '#2645e0', '#099441', '#e5e500', '#cc8400', '#cc0000',
'#737373', '#e53fe5', '#0000FF', '#4479e1', '#60A830', '#ffff00','#e69500', '#ff0000',
'#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40', '#ffff7f', '#ffa500', '#ff4c4c')
ui <- fluidPage(
h1("WELCOME TO THE TEST APP", style = 'text-align: center; font-weight:bold' ),
br(),
h3("STATIC PART: doesn't fire on startup, great!", style = 'font-weight:bold'),
div(colourpicker::colourInput(inputId = 'StaticColor', label = NULL, palette = "limited", allowedCols = THECOLORS, value = THECOLORS[14], showColour = "background", returnName = TRUE),
style = " height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"),
br(),
h3("Dynamic part: fires on render, NOT great!", style = 'font-weight:bold'),
actionButton(inputId = 'Tester', label = 'Click me'),
br(),
uiOutput('colorbutton')
)
server <- function(input, output, session) {
values <- reactiveValues()
values$mycolors <- THECOLORS
observeEvent(input$Tester, { values$NrofButtons <- sample(1:10, 1) })
observeEvent(values$NrofButtons, {
COLElement <- function(idx){sprintf("COL_button-%s-%d",values$NrofButtons,idx)}
output$colorbutton <- renderUI({
lapply(1:values$NrofButtons, function(x) {
div(colourpicker::colourInput(inputId = COLElement(x), label = NULL, palette = "limited", allowedCols = values$mycolors, value = values$mycolors[x], showColour = "background", returnName = TRUE),
style = " height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px") })
})
lapply(1:values$NrofButtons, function(x) { observeEvent(input[[COLElement(x)]], { print(input[[COLElement(x)]] )}, ignoreInit = T) }) # make observer for each button
})
observeEvent(input[['StaticColor']], { print(input[['StaticColor']] )}, ignoreInit = T)
}
shinyApp(ui,server)
Renders should always be by themselves and be data driven, not event driven -- so I've made the render require the number of colors to be defined before rendering. Of course the number of colors aren't defined until the observeEvent is fired by clicking the button.
Overall there is still the issue that every time the button is clicked more observers are created for the same ID, working on a way to destroy these automatically on a subsequent click of the tester button.
The key addition was a ignoreInit = TRUE in your observeEvent(input$Tester, {...}) observer.
library(shiny)
library("colourpicker")
THECOLORS <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400', '#990000',
'#505050', '#a02ca0', '#000099', '#2645e0', '#099441', '#e5e500', '#cc8400', '#cc0000',
'#737373', '#e53fe5', '#0000FF', '#4479e1', '#60A830', '#ffff00','#e69500', '#ff0000',
'#b2b2b2', '#eb6ceb', '#6666ff', '#d0a3ff', '#9FDA40', '#ffff7f', '#ffa500', '#ff4c4c')
ui <- fluidPage(
h1("WELCOME TO THE TEST APP", style = 'text-align: center; font-weight:bold' ),
br(),
h3("STATIC PART: doesn't fire on startup, great!", style = 'font-weight:bold'),
div(colourpicker::colourInput(inputId = 'StaticColor', label = NULL, palette = "limited", allowedCols = THECOLORS, value = THECOLORS[14], showColour = "background", returnName = TRUE),
style = " height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"),
br(),
h3("Dynamic part: fires on render, NOT great!", style = 'font-weight:bold'),
actionButton(inputId = 'Tester', label = 'Click me'),
br(),
uiOutput('colorbutton')
)
COLElement <- function(idx) sprintf("COL_button-%d", idx)
server <- function(input, output, session) {
values <- reactiveValues(previous_max = 1)
observeEvent(input$Tester, {
values$NrofButtons <- sample(1:10, 1)
# reset counters for all observers
for (i in seq(values$NrofButtons)) {
values[[sprintf("observer%d_renders", i)]] <- 0L
}
# only initialize incremental observers
lapply(values$previous_max:values$NrofButtons, function(x) {
observeEvent(input[[COLElement(x)]], {
# only execute the second time, since the `ignoreInit` isn't obeyed
if (values[[sprintf("observer%d_renders", x)]] > 0) {
print(input[[COLElement(x)]] )
} else {
values[[sprintf("observer%d_renders", x)]] <- 1L
}
}, ignoreInit = TRUE)
}) # make observer for each button
# record the max
values$previous_max <- max(values$previous_max, max(values$NrofButtons))
}, ignoreInit = TRUE)
output$colorbutton <- renderUI({
req(length(values$NrofButtons) > 0)
lapply(1:values$NrofButtons, function(x) {
div(colourpicker::colourInput(
inputId = COLElement(x)
, label = NULL
, palette = "limited"
, allowedCols = THECOLORS
, value = THECOLORS[x]
, showColour = "background"
, returnName = TRUE
)
, style = " height: 30px; width: 30px; border-radius: 6px; border-width: 2px; text-align:center; padding: 0px; display:block; margin-bottom: 10px"
)
})
})
observeEvent(input$StaticColor, {
print(input$StaticColor )
}, ignoreInit = TRUE)
}
shinyApp(ui,server)
Is it possible to have a dropdown list in Shiny where you can select multiple values? I know selectInput has the option to set multiple = T but I don't like it that all selected option are visible in the screen, especially since I have over 40. The same holds for checkboxGroupInput(), which I like more but still all selected values are shown. Isn't it just possible to get a drop-down like the one I copied from Excel below, rather than the examples of Shinys selectInput and checkboxGroupInput() thereafter?
EDIT : This function (and others) is available in package shinyWidgets
Hi I wrote this dropdownButton function once, it create a bootstrap dropdown button (doc here), the results looks like :
Here is the code :
# func --------------------------------------------------------------------
dropdownButton <- function(label = "", status = c("default", "primary", "success", "info", "warning", "danger"), ..., width = NULL) {
status <- match.arg(status)
# dropdown button content
html_ul <- list(
class = "dropdown-menu",
style = if (!is.null(width))
paste0("width: ", validateCssUnit(width), ";"),
lapply(X = list(...), FUN = tags$li, style = "margin-left: 10px; margin-right: 10px;")
)
# dropdown button apparence
html_button <- list(
class = paste0("btn btn-", status," dropdown-toggle"),
type = "button",
`data-toggle` = "dropdown"
)
html_button <- c(html_button, list(label))
html_button <- c(html_button, list(tags$span(class = "caret")))
# final result
tags$div(
class = "dropdown",
do.call(tags$button, html_button),
do.call(tags$ul, html_ul),
tags$script(
"$('.dropdown-menu').click(function(e) {
e.stopPropagation();
});")
)
}
And an example :
# app ---------------------------------------------------------------------
library("shiny")
ui <- fluidPage(
tags$h1("Example dropdown button"),
br(),
fluidRow(
column(
width = 6,
dropdownButton(
label = "Check some boxes", status = "default", width = 80,
checkboxGroupInput(inputId = "check1", label = "Choose", choices = paste(1:26, ") Choice ", LETTERS))
),
verbatimTextOutput(outputId = "res1")
),
column(
width = 6,
dropdownButton(
label = "Check some boxes", status = "default", width = 80,
actionButton(inputId = "a2z", label = "Sort A to Z", icon = icon("sort-alpha-asc")),
actionButton(inputId = "z2a", label = "Sort Z to A", icon = icon("sort-alpha-desc")),
br(),
actionButton(inputId = "all", label = "(Un)select all"),
checkboxGroupInput(inputId = "check2", label = "Choose", choices = paste(1:26, ") Choice ", LETTERS))
),
verbatimTextOutput(outputId = "res2")
)
)
)
server <- function(input, output, session) {
output$res1 <- renderPrint({
input$check1
})
# Sorting asc
observeEvent(input$a2z, {
updateCheckboxGroupInput(
session = session, inputId = "check2", choices = paste(1:26, ") Choice ", LETTERS), selected = input$check2
)
})
# Sorting desc
observeEvent(input$z2a, {
updateCheckboxGroupInput(
session = session, inputId = "check2", choices = paste(26:1, ") Choice ", rev(LETTERS)), selected = input$check2
)
})
output$res2 <- renderPrint({
input$check2
})
# Select all / Unselect all
observeEvent(input$all, {
if (is.null(input$check2)) {
updateCheckboxGroupInput(
session = session, inputId = "check2", selected = paste(1:26, ") Choice ", LETTERS)
)
} else {
updateCheckboxGroupInput(
session = session, inputId = "check2", selected = ""
)
}
})
}
shinyApp(ui = ui, server = server)
In bonus I put the ascending/descending sorting thingy in the second dropdown buttons.
EDIT Mar 22 '16
To split yours checkboxes into multiple columns you can do the split yourself with fluidRow and columns and multiples checkboxes, you just have to bind the values server-side.
To implement scrolling put your checkboxes into a div with style='overflow-y: scroll; height: 200px;'.
Look at this example :
library("shiny")
ui <- fluidPage(
tags$h1("Example dropdown button"),
br(),
fluidRow(
column(
width = 6,
dropdownButton(
label = "Check some boxes", status = "default", width = 450,
tags$label("Choose :"),
fluidRow(
column(
width = 4,
checkboxGroupInput(inputId = "check1a", label = NULL, choices = paste0(1:10, ") ", LETTERS[1:10]))
),
column(
width = 4,
checkboxGroupInput(inputId = "check1b", label = NULL, choices = paste0(11:20, ") ", LETTERS[11:20]))
),
column(
width = 4,
checkboxGroupInput(inputId = "check1c", label = NULL, choices = paste0(21:26, ") ", LETTERS[21:26]))
)
)
),
verbatimTextOutput(outputId = "res1")
),
column(
width = 6,
tags$style(".container { border:2px solid steelblue; width: 100%; height: 200px; overflow-y: scroll; }"),
dropdownButton(
label = "Check some boxes", status = "default", width = 120,
tags$div(
class = "container",
checkboxGroupInput(inputId = "check2", label = "Choose", choices = paste0(1:26, ") ", LETTERS))
)
),
verbatimTextOutput(outputId = "res2")
)
)
)
server <- function(input, output, session) {
valuesCheck1 <- reactiveValues(x = NULL)
observeEvent(input$check1a, valuesCheck1$x <- unique(c(valuesCheck1$x, input$check1a)))
observeEvent(input$check1b, valuesCheck1$x <- unique(c(valuesCheck1$x, input$check1b)))
observeEvent(input$check1c, valuesCheck1$x <- unique(c(valuesCheck1$x, input$check1c)))
output$res1 <- renderPrint({
valuesCheck1$x
})
output$res2 <- renderPrint({
input$check2
})
}
shinyApp(ui = ui, server = server)
Firstly, lot of thanks for this dropdownButton function. It's very useful!
Secondly, i tried to use it into shiny dashboard sidebarmenu, but the default characters' style is "color:white" (because of dark background). That takes me a couple of hour to understand that can be changed inside your function, more precisly in html_ul stuff. Here's the line of interest, with color:black :
lapply(X = list(...), FUN = tags$li, style = "margin-left: 10px; margin-right: 10px; color:black")
Quite simple... But when you don't know it (R is the only language I know)... So, I hope this will help any other css-ignorant (and/or HTML?) like me!
Cheers!
There are a couple questions in the comments related the the dropdownButton (worked great for me, thank you) about how to create a scrolling bar on the dropdown. Sorry I don't have reputation to reply in the comments directly.
Try tweaking the relevant ID in your styles.css, for whatever object you put in the dropdownButton. So for the example, the checkboxGroupInput ID needs to have:
#check1
{
height: 200px;
overflow: auto;
}
Edit:
To call the styles.css in the ui.R:
navbarPage("Superzip", id="nav",
tabPanel("Interactive map",
div(class="outer",
tags$head(
# Include our custom CSS
includeCSS("styles.css")
),
leafletOutput("map", width="100%", height="100%"),
...
And the styles.css, with the auto overflow for the inputID ttype and chain:
input[type="number"] {
max-width: 80%;
}
div.outer {
position: fixed;
top: 41px;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
padding: 0;
}
/* Customize fonts */
body, label, input, button, select {
font-family: 'Helvetica Neue', Helvetica;
font-weight: 200;
}
h1, h2, h3, h4 { font-weight: 400; }
#controls {
/* Appearance */
background-color: white;
padding: 0 20px 20px 20px;
cursor: move;
/* Fade out while not hovering */
opacity: 0.65;
zoom: 0.9;
transition: opacity 500ms 1s;
}
#controls:hover {
/* Fade in while hovering */
opacity: 0.95;
transition-delay: 0;
}
#data_inputs {
/* Appearance */
background-color: white;
padding: 0 20px 20px 20px;
cursor: move;
/* Fade out while not hovering */
opacity: 0.65;
zoom: 0.9;
transition: opacity 500ms 1s;
}
#data_inputs:hover {
/* Fade in while hovering */
opacity: 0.95;
transition-delay: 0;
}
/* Position and style citation */
#cite {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 12px;
}
#cite {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 12px;
}
#ttype
{
height: 200px;
overflow: auto;
}
#chain
{
height: 200px;
overflow: auto;
}
."form-group shiny-input-checkboxgroup shiny-input-container"
{
height: 50px;
overflow: auto;
}
/* If not using map tiles, show a white background */
.leaflet-container {
background-color: white !important;
}
For future visitors that might need similar solutions, a good option could be the selectizeInput .
Pros:
You can set the list length
Is a dropdown function
User can select one or more choices by searching the list
or by typing in the box.
For more information check the above link. Hope this will help.
Cheers!