R Shiny CondtionalPanel not using CSS stylesheet - css

I'm using R Shiny to build a web application.
I'm using conditionPanels to (sometimes) show a pivot table depending on the type of object df.
As shown below, if the pivot table is shown within a conditionalpanel, the css is simply ignored and the Pivot table is shown in default style. But if I include a second pivottable, not rendered in the conditionalpanel, both pivottables are in the style as described in the custom.css.
How can I make sure that the stylesheet is used for the first pivottable when there is not second one?
# Server.R
server <- shinyServer(function(input, output,session){
df <- data.frame(col1 = c('a','b','c'),
col2 = c(1,2,3))
## Output PivotTable
output$pivotTable <- rpivotTable::renderRpivotTable({
rpivotTable(data = df,
aggregatorName = 'Sum',
rendererName = 'Table')
})
## Output PivotTable2
output$pivotTable2 <- rpivotTable::renderRpivotTable({
rpivotTable(data = df,
aggregatorName = 'Sum',
rendererName = 'Table')
})
condition <- ifelse(is.data.frame(df), 'true', 'false')
## Output PivotTable
output$panelTable <- renderUI({
conditionalPanel(
condition,
rpivotTableOutput("pivotTable")
)
})
})
# UI.R:
ui <- dashboardPage(
title = "",
## Header content + dropdownMenu
dashboardHeader(
title = tags$b(""),
titleWidth = 250
),
## Sidebar content
dashboardSidebar(
width = 250,
sidebarMenu(
id = "tabs",
menuItem("tab1", tabName = "tab", icon = icon("table"))
)
),
## Body content
dashboardBody(
tags$head(tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")),
tabItems(
tabItem(tabName = "tab",
div(
uiOutput('panelTable')
),
div(
rpivotTableOutput("pivotTable2")
)
)
)
)
)
# Create Shiny object
shinyApp(ui = ui, server = server)
CSS:
/* Adjust css of pivot table */
#pivotTable{
overflow-x: scroll;
overflow-y: scroll;
}
.pvtRows, .pvtCols {
background: #FAFAFA none repeat scroll 0 0;
}
table.pvtTable tbody tr th, table.pvtTable thead tr th {
background: #FFFFFF;
}
.pvtAxisContainer li span.pvtAttr {
background: rgba(147,255,53,0.8);
}

HI your problem is that your css is being overruled from css rules generated be pivotTable to over rule this add !important after each rule like this
#pivotTable{
overflow-x: scroll;
overflow-y: scroll;
}
.pvtRows, .pvtCols {
background: #FAFAFA none repeat scroll 0 0 !important;
}
table.pvtTable tbody tr th, table.pvtTable thead tr th {
background: #FFFFFF!important;
}
.pvtAxisContainer li span.pvtAttr {
background: rgba(147,255,53,0.8) !important;
}
hope this helps!

I think you can try to define class inside the div's.
For example:
div(class = "pvtRows pvtAxisContainer",
uiOutput('panelTable')
)

Related

Remove all data tables using from highcharter export menu

I am not sure if this is a bug or a feature of highcharter. I have a simple app which allow users to quickly see the data used to plot the chart. Let's say the user select the "View data table" option from the right menu. Once the data table appears, they then change one of the drop down inputs. A new set of data is plotted. The user then again select "View data table", and another set of data table appears.
Now if the user select "Hide data table", only one of the tables is removed. I wonder if there is a way to remove them all?
library(highcharter)
library(tidyverse)
library(shiny)
ui <- fluidPage(
selectInput("First", label = "First Variable", width = "100%",
choices = colnames(iris)),
selectInput("Second", label = "Second", width = "100%",
choices = colnames(iris)),
highchartOutput("hchartcont")
)
server = function(input, output) {
output$hchartcont <- renderHighchart({
df <- iris %>% select(x = input$First, y = input$Second)
hchart(df, "line", hcaes(x, y)) %>%
hc_exporting(enabled = TRUE)
})
}
shinyApp(ui = ui, server = server)
This is a workaround. When you update the chart, the table will be removed using htmlwidgets onRender().
Unfortunately, if the page is resized enough to trigger re-rendering, the table will be removed.
This is the updated output$highchartcont from your server.
output$hchartcont <- renderHighchart({
df <- iris %>% select(x = input$First, y = input$Second)
hchart(df, "line", hcaes(x, y)) %>%
hc_exporting(enabled = TRUE) %>%
htmlwidgets::onRender(
"function(el, x) {
Highcharts.addEvent(Highcharts.Chart, 'render', function() {
if($('div.highcharts-data-table').length) { /* does it exist? */
$('div.highcharts-data-table').remove(); /* then remove it */
}
});
}")})
If you weren't aware, you can style this table to make it more aesthetically pleasing. For example...
ui <- fluidPage(
tagList(
tags$style(HTML(
".highcharts-data-table tr:nth-child(even),
.highcharts-data-table thead tr {
background: #f8f8f8;
}
.highcharts-data-table td,
.highcharts-data-table th,
.highcharts-data-table caption {
border: 1px solid silver;
padding: 0.5em;
}
.highcharts-data-table tr {
cursor: pointer;
}
.highcharts-data-table tr:hover {
background: #eff;
}
.highcharts-data-table table {
border-collapse: collapse;
border-spacing: 0;
min-width: 200px;
max-width: 100%;
margin-top: 10px;
}")),
selectInput("First", label = "First Variable", width = "100%",
choices = colnames(iris)),
selectInput("Second", label = "Second", width = "100%",
choices = colnames(iris)),
highchartOutput("hchartcont")
))

How to replenish the bucket list when running the sortable package?

I'm exploring ways to visualize a mathematical process (a series of sequential computations) and the sortable package may be the answer, with modifications. The below reproducible code is pulled from the basic examples of how to use sortable found online and in its reference manual (with a table output added so I can start manipulating the data frame).
I'm trying to see if there's a way to not deplete the bucket list as the user drags and drops from the left panel (labeled "Pool" in my example) to the right panel (labeled "Pool Ranking" in my example). So looking at the image at the bottom, the user can drag/drop items A, B, C, D, E, as many times as desired. The right "drag to" panel then grows longer than the left "drag from" panel due to the repetition of elements dragged/dropped.
Is this possible in sortable? How can it be done? Or should I be looking at other packages instead?
If I can figure this out, my next step will be to add another "drag from" panel of mathematical formulas to drag to the right of the dragged-to label elements A - E.
Reproducible code:
library(shiny)
library(sortable)
ui <-
fluidPage(
tags$head(tags$style(HTML("
.column_2 {
counter-reset: rank;
}
.column_2 .rank-list-item::before {
counter-increment: rank;
content: counter(rank) '. ';
}
"))),
htmlOutput("rankingForm"),
helpText(h5(strong("Output to table:"))),
tableOutput("table1")
)
server <- function(input, output, session) {
output$rankingForm <- renderUI({
fluidRow(
column(tags$b("Pool Ranking"), width = 12,
bucket_list(header = "Drag to the right from the Pool below to rank.",
group_name = "bucket_list_group", orientation = "horizontal",
add_rank_list("Pool:",
labels = c("A","B","C","D","E"),
input_id = "rank_list_1"
),
add_rank_list("Pool Ranking:", labels = NULL,
input_id = "rank_list_2")
)
)
)
})
output$table1 <- renderTable({input$rank_list_2})
}
shinyApp(ui=ui, server=server)
Through these listed StackOverflow posts and solutions, I arrived at the "Working solution" shown at the bottom:
Related posts:
vladimir_orbucina request at https://github.com/rstudio/sortable/issues/45 and
How to pull list elements from HTML/CSS and into an R data frame? and
How to make the label in the sortable package add_rank_list function reactive? and
Any creative ways to add rank ordering numbering to this simple sortable package example?
And an important link that explains cloning: https://rstudio.github.io/sortable/articles/cloning.html
Working solution:
library(shiny)
library(sortable)
library(htmlwidgets)
icons <- function(x) {lapply(x,function(x){tags$div(tags$strong(x))})}
ui <- fluidPage(
# Below solution provided by I|O on Jun 1, 2022:
tags$head(
tags$style(HTML('
#drag_from > div {cursor: move; #fallback
cursor: grab; cursor: pointer;
}
#drag_to > div {cursor: move; #fallback
cursor: grab; cursor: pointer;
}
#drag_to {list-style-type: none; counter-reset: css-counter 0;}
#drag_to > div {counter-increment: css-counter 1;}
#drag_to > div:before {content: counter(css-counter) ". ";}
')
)
),
div(
style = "margin-top: 2rem; width: 60%; display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: start;",
div(
div(
class = "panel panel-default",
div(class = "panel-heading", "Drag from here"),
div(
class = "panel-body",
id = "drag_from",
icons(c("A", "B", "C", "D", "E"))
)
),
),
div(
div(
class = "panel panel-default",
div(class = "panel-heading", "Drag to here"),
div(
class = "panel-body",
id = "drag_to"
)
)
)
),
sortable_js(
"drag_from",
options = sortable_options(
group = list(
pull = "clone",
name = "group1",
put = FALSE
)
)
),
sortable_js(
"drag_to",
options = sortable_options(
group = list(
group = "group1",
put = TRUE,
pull = TRUE
),
onSort = sortable_js_capture_input(input_id = "selected") # << solution by stefan on Jun 01, 2022
)
),
helpText(h5(strong("Output to table:"))),
tableOutput("table1")
)
server <- function(input, output) {
output$table1 <- renderTable({input$selected})
}
shinyApp(ui, server)

too many scrollbars Shiny rhandsontable

I'm using rhandsontable with dqshiny to display a large table in an app.
I put some code below. In this code, there are maybe 4 scroll-bars. 2 in the table and 2 on the outer page.
I only want 2 scroll-bars, can someone help? I have been messing with the CSS for hours to try and get the filters to show but also have scrollbars.
library(shiny)
library(rhandsontable)
library(shinythemes)
library(shinyjs)
library(dqshiny)
df = data.frame(hello1 = seq(100), stringsAsFactors = FALSE)
df2 = df
for(i in 1:30){
df = cbind(df, df2)
}
names(df) = paste0(names(df), seq(20))
shinyApp(
ui = fluidPage(
theme = shinytheme("cerulean"),
navbarPage("sample Rhandsontable Page", selected = "tab01",
id = "navbar",
position = "fixed-top",
tags$style(type="text/css", "body {padding-top: 70px;}"),
tags$head(
tags$style(
#"body {overflow-y: scroll;}"
"body {overflow: visible;}"
)
),
tags$head(
tags$style(type = "text/css", ".container-fluid {padding-left:0px;
padding-right:0px; margin-right:0px; margin-left:0px;}")
),
useShinyjs(),
tabPanel("tab01",
tags$head(tags$style("#randomTable-filters {overflow:visible !important;}")),
tags$style('#randomTableTags * { white-space: nowrap}'),
tags$style('.shiny-html-output * {width = 100% }'),
div(id='randomTableTags', style="overflow: visible;",
dq_handsontable_output("randomTable")
)
)
)
),
server = function(input, output, session) {
dq_render_handsontable(
"randomTable",
df,
filters = "T",
page_size = c(500L, 1000L),
width_align = TRUE,
horizontal_scroll = FALSE,
table_param = list(highlightRow = TRUE, autoColumnSize = TRUE),
)
}
)
here is an image of the output:
If you have trouble with dqshiny, you can run:
library(devtools)
devtools::install_github('daqana/dqshiny', upgrade = 'always')
Solution 1: You can add this in your css file. It should be applicable to all handsontable in your app.
.handsontable {
overflow: hidden;
}
Solution 2: You can use stretcH in your code to avoid extra scroll bars.
rhandsontable(data,stretchH = "all",stretchV = "all")

How can I display a dataframe in a Shiny app as a grid, and not a table?

I have some data in a dataframe. I can display the dataframe as a table with DataTables.
However, I would like to display the data as a grid with N columns, so that every N rows from the data frame are shown in the same row.
Show data as a grid:
As shown in the image above, I have gotten a grid to show up by using HTML to render the data frame directly.
But the next step is where I am stuck, which is I want to be able to show a modal dialog when a cell in the grid is clicked.
I have that working in the data table, but I haven't been able to figure out how to make a div clickable, such that when handling the event I know which cell was clicked?
library("shiny")
library("tidyr")
library("tidyverse")
library("dplyr")
library("shinydashboard")
# generate html grid from data frame
getHTML <- function (frames) {
innerhtml = '<div class="grid-container">'
for (row in 1:(nrow(frames))) {
id <- frames[row, "id"]
name <- frames[row, "names"]
row_html = '<div class="grid-item">'
row_html = paste(row_html, '<span>Name: ' , name, "id ", row , '</span>')
row_html = paste(row_html, '</div>')
innerhtml = paste(innerhtml, row_html)
}
paste(innerhtml, "</div>")
return (innerhtml)
}
#show modal dialog for player id and name
plotModal <- function(id, name) {
modalDialog(
p(paste("Player # ", id, ", " , name,", was clicked")),
title = paste("Player " , id),
easyClose = TRUE
)
}
ui <- dashboardPage(
# Application title
dashboardHeader(title = "Dashboard"),
dashboardSidebar(
h3("Filters")
),
dashboardBody(
tags$head(tags$style(HTML('
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
}'))),
fluidRow(
box(title="Render as table", column(width=12, DT::dataTableOutput("player_table"))),
box(title="Render as Grid", column(width=12, uiOutput("player_grid")))
)
)
)
server <- function(input, output, session) {
# data to be rendered
frames = data.frame(names= c("james","kyle", "sally","hannah","jeff","kurt"), ids=c(1:6))
output$player_table <- DT::renderDataTable({
DT::datatable(frames, rownames=FALSE, selection = 'single')
})
#when a row in the table is clicked, show popup
observeEvent(input$player_table_cell_clicked, {
info = input$player_table_cell_clicked
# do nothing if not clicked yet, or the clicked cell is not in the 1st column
if (is.null(info$value)) {
return()
}
row = frames[info$row, ]
showModal(plotModal(row$id, row$names))
})
output$player_grid <- renderUI ({
HTML(getHTML(frames))
})
}
# Create Shiny app ----
shinyApp(ui, server,options=list(host="0.0.0.0", port=8015))
Here is a way:
library(shiny)
library(shinydashboard)
js <- "
$(document).ready(function(){
$('body').on('click', '.grid-item span', function(){
var name = $(this).data('name'),
id = $(this).data('id');
Shiny.setInputValue('cell', {name: name, id: id});
});
});
"
# generate html grid from data frame
getHTML <- function (frames) {
innerhtml = '<div class="grid-container">'
for (row in 1:(nrow(frames))) {
id <- frames[row, "ids"]
name <- frames[row, "names"]
row_html = '<div class="grid-item">'
cell <- sprintf("<span data-name='%s' data-id='%s'>Name: %s - id: %s</span>",
name, id, name, id)
row_html = paste(row_html, cell)
row_html = paste(row_html, '</div>')
innerhtml = paste(innerhtml, row_html)
}
paste(innerhtml, "</div>")
return (innerhtml)
}
#show modal dialog for player id and name
plotModal <- function(id, name) {
modalDialog(
p(paste("Player # ", id, ", " , name,", was clicked")),
title = paste("Player " , id),
easyClose = TRUE
)
}
ui <- dashboardPage(
# Application title
dashboardHeader(title = "Dashboard"),
dashboardSidebar(
h3("Filters")
),
dashboardBody(
tags$head(tags$style(HTML('
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
}')),
tags$script(HTML(js))),
fluidRow(
box(title="Render as Grid", column(width=12, uiOutput("player_grid")))
)
)
)
server <- function(input, output, session) {
# data to be rendered
frames = data.frame(
names= c("james","kyle", "sally","hannah","jeff","kurt"),
ids=c(1:6)
)
#when a row in the table is clicked, show popup
observeEvent(input$cell, {
showModal(plotModal(input$cell$id, input$cell$name))
})
output$player_grid <- renderUI ({
HTML(getHTML(frames))
})
}
# Create Shiny app ----
shinyApp(ui, server)

Resetting modal when closing it in a shiny app

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)

Resources