glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
633 stars 80 forks source link

JS rendering of sparkline within nested rows #151

Open Atan1988 opened 3 years ago

Atan1988 commented 3 years ago

Hello,

Thank you for this great package. The ability to include nested reactable within details is super helpful, but the rendering is too slow for interactive usage within shiny apps. Would it be possible to convert the R rendering below into JS rendering? I found some example code that would be able to convert R rendering of sparkline to JS rendering at the first level, but it doesn't work within the nested rows.

Thanks, Allen

library(dplyr)
library(sparkline)

data1 <- chickwts %>% 
  dplyr::mutate(feed = "All") %>% 
  group_by(feed) %>%
  summarise(weight = list(weight)) %>%
  mutate(boxplot = NA, sparkline = NA)

data2 <- chickwts %>%
  group_by(feed) %>%
  summarise(weight = list(weight)) %>%
  mutate(boxplot = NA, sparkline = NA)

reactable(data1, columns = list(
  weight = colDef(cell = function(values) {
    sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight))
  }),
  boxplot = colDef(cell = function(value, index) {
    sparkline(data1$weight[[index]], type = "box")
  }),
  sparkline = colDef(cell = function(value, index) {
    sparkline(data1$weight[[index]])
  })), 
  details = function(index) {
    htmltools::div(tyle = "padding: 16px", 
      reactable(data2, columns = list(
        weight = colDef(cell = function(values) {
          sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight))
        }),
        boxplot = colDef(cell = function(value, index) {
          sparkline(data2$weight[[index]], type = "box")
        }),
        sparkline = colDef(cell = function(value, index) {
          sparkline(data2$weight[[index]])
        }) 
      )))
  }
)
glin commented 3 years ago

Hi, the sparkline widgets not rendering in nested tables was a bug that was recently fixed (https://github.com/glin/reactable/issues/125). If you install the development version of reactable from GitHub, it should work now.

As for rendering sparklines with a JavaScript function to speed up rendering - that should be possible, but it's tricky and I don't have any example of it myself. You'll either have to render the sparklines as raw HTML (with colDef(html = TRUE)), or as a React element. The second method isn't documented at all, but you could check out Kent's dataui package for an example: https://timelyportfolio.github.io/dataui/articles/dataui_reactable.html

Atan1988 commented 3 years ago

Thank you for the reply. I tired the dataui pakcage actually, but ran into similar speed issue when rendering my actual data. I was able to adapt an example on stacked overflow for JS rendering of the main table, but not the nested table. ` library(dplyr) library(sparkline)

data1 <- chickwts %>% dplyr::mutate(feed = "All") %>% group_by(feed) %>% summarise(weight = list(weight)) %>% mutate(boxplot = NA, sparkline = NA)

data2 <- chickwts %>% group_by(feed) %>% summarise(weight = list(weight)) %>% mutate(boxplot = NA, sparkline = NA)

R rendering

reactable(data1, columns = list( weight = colDef(cell = function(values) { sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight)) }), boxplot = colDef(cell = function(value, index) { sparkline(data1$weight[[index]], type = "box") }), sparkline = colDef(cell = function(value, index) { sparkline(data1$weight[[index]]) })), details = function(index) { htmltools::div(tyle = "padding: 16px", reactable(data2, columns = list( weight = colDef(cell = function(values) { sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight)) }), boxplot = colDef(cell = function(value, index) { sparkline(data2$weight[[index]], type = "box") }), sparkline = colDef(cell = function(value, index) { sparkline(data2$weight[[index]]) }) ))) } )

JS rendering

function to generate sparkline html string from a list of column

sparkline_str_generator <- function(x, type = 'line', add_opts = "") { part1 <- ''

out <- list(length = length(x)) out <- purrr::map(seq_len(length(x)), function(i){ id <- c(letters, seq_len(10) - 1) %>% sample(26) %>% paste(collapse = "") vals <- x[[i]] vals <- ifelse(is.infinite(vals)|is.nan(vals), NA, vals) vals <- as.character(vals); vals <- ifelse(is.na(vals), 'null', vals) vals <- paste(vals, collapse = ",") paste0(part1, id, part2, id, part3, vals, dplyr::case_when(type == 'line' ~ part4l, type == "bar" ~ part4bar, type == 'box' ~part4box), add_opts, end_str) })

return(out) } data1$bar <- sparkline_str_generator(data1$weight, type = 'bar', add_opts = ',"chartRangeMin":0') data1$sparkline <- sparkline_str_generator(data1$weight, type = 'line', add_opts = ',"chartRangeMin":0') data1$boxplot <- sparkline_str_generator(data1$weight, type = 'box', add_opts = ',"chartRangeMin":0')

columns_def <- purrr::map(c('bar', 'sparkline', 'boxplot'), ~colDef(html = TRUE, cell = function(value, index) { return(htmltools::HTML(value))})) names(columns_def) <- c('bar', 'sparkline', 'boxplot')

data2$bar <- sparkline_str_generator(data2$weight, type = 'bar', add_opts = ',"chartRangeMin":0') data2$sparkline <- sparkline_str_generator(data2$weight, type = 'line', add_opts = ',"chartRangeMin":0') data2$boxplot <- sparkline_str_generator(data2$weight, type = 'box', add_opts = ',"chartRangeMin":0')

rendering main table works

reactable(data1 %>% dplyr::select(-weight), columns = columns_def ) %>% spk_add_deps()

rendering the sub table also works

reactable(data2 %>% dplyr::select(-weight), columns = columns_def) %>% spk_add_deps()

rendering nested doesn't seem to work -- sparklines show up at the main row but not the nested rows

reactable(data1 %>% dplyr::select(-weight), columns = columns_def, details = function(index) { htmltools::div(tyle = "padding: 16px", reactable(data2 %>% dplyr::select(-weight), columns = columns_def) %>% spk_add_deps() ) } ) %>% spk_add_deps() `