glin / reactable

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

Customize Scrollbar #306

Open Adeiko opened 1 year ago

Adeiko commented 1 year ago

Hi!

I've been trying to customize the scroll bars that show up in case you define a height/width and the container where the reactable is in is smaller than the table.

image

I've Tried adding a couple of javascript libraries that try to customize the scroll bar from jquery yo vanilla jscript, and they seem to work in any other element of the website but not on the reactable one.

https://grsmto.github.io/simplebar/ https://manos.malihu.gr/jquery-custom-content-scroller/

I'm rendeing the reactable using htmltools::save_html() on the table that i've defined a height and the fullwidth option is enabled.

I do not know is this counts as a reprex

I do not know is this counts as a reprex, but I've tried something like this:

Using this template from simplebar I've tried to do a reprex that generates an html file with a reactable

https://codepen.io/trstnpr/pen/abomLGa

snip_head <- "<link rel='stylesheet' href='https://unpkg.com/simplebar@latest/dist/simplebar.css'>
<link rel='stylesheet' href='./style.css'>"

snip_start <-"<div class='container' data-simplebar='init'><div class='simplebar-wrapper' style='margin: -5px;'><div class='simplebar-height-auto-observer-wrapper'><div class='simplebar-height-auto-observer'></div></div><div class='simplebar-mask'><div class='simplebar-offset' style='right: 0px; bottom: 0px;'><div class='simplebar-content-wrapper' tabindex='0' role='region' aria-label='scrollable content' style='height: 100%; overflow: hidden scroll;'><div class='simplebar-content' style='padding: 5px;'>"

snip_end <- "</div></div></div></div><div class='simplebar-placeholder' style='width: auto; height: 820px;'></div></div><div class='simplebar-track simplebar-horizontal' style='visibility: hidden;'><div class='simplebar-scrollbar' style='width: 0px; display: none;'></div></div><div class='simplebar-track simplebar-vertical' style='visibility: visible;'><div class='simplebar-scrollbar' style='height: 205px; transform: translate3d(0px, 97px, 0px); display: block;'></div></div></div>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js'></script>
<script src='https://unpkg.com/simplebar@latest/dist/simplebar.min.js'></script><script src='./script.js'></script>"

htmldata <- htmlwidgets::prependContent(
  iris |> reactable::reactable(elementId = "testtable", fullWidth = TRUE,height = "300px",static=FALSE),
  htmltools::tags$head(
    htmltools::HTML(
      snip_head
    )
  ),
  htmltools::HTML(snip_start)
) |>
  htmlwidgets::appendContent(
    htmltools::HTML(
      snip_end
    )
  )|>htmltools::save_html(file="testtable/test.html")

Inside script.js in that same directory:

new SimpleBar(document.querySelector('div'));

inside style.css in the same directory:

.container {
  height: 400px;
  width: 400px;
  border: 1px  solid #eee;
  padding: 5px;
  overflow-y: auto;
}

If I replace the div from the reactable with a lorem ipsum it seems to work perfectly. image

Any insight on what could be happening?

glin commented 1 year ago

Hi, I probably won't be able to help much here. I can't run that code due to a syntax error, but generally for scrollbar libraries, you'll first want to find the container with the scrollbar on it so you can target it with the library. In reactable tables, that's the table body element with a rt-tbody class on it, which you can select like document.querySelector('#testtable .rt-tbody'). I don't see any reference to the table body in that snippet, so I'll guess that's the main problem with getting this working.

The other important thing is that this will be relying on undocumented internal details and CSS classes without any stability guarantee, so definitely use at your own risk, use renv to protect your code from unexpected changes.

The safest way to add a custom scrollbar would be to build it into the package, but unfortunately, that's likely not going to happen. I tried to implement custom scrollbars in the past to work around a sticky header issue, but found too many issues with every library I tried. I think SimpleBar was one of them, and adding it broke sticky columns if I remember correctly. In the end, I figured it was best to just leave it up to the browser. There are other complications with changing the default scrollbar as well, around usability, accessibility, veering too far off from the native look and feel of each browser that does scrollbars differently.

Adeiko commented 1 year ago

Yeah sorry there was a comma missing as an error on the reprex.

Yeah I didn't try selecting the ".rt-tbody" on the startup since when the script runs it would still not exist, that is why I tried targetting the container. Tried to do it manually from the console and still has doesn't work. image

Yeah I totally agree this is an edge case and it's not worth investing any of your time. It was more curiosity if reactable did something strange with the overflow than anything else.

glin commented 1 year ago

For scripts that depend on the widget to be fully rendered before running, htmlwidgets has these useful htmlwidgets::onRender() and htmlwidgets::onStaticRenderComplete() functions. There are some examples of this in the JavaScript API docs.

For example, something like this should work:

library(htmltools)
library(reactable)

browsable(
  tagList(
    tags$head(
      tags$link(rel = "stylesheet", href = "https://unpkg.com/simplebar@latest/dist/simplebar.css"),
      tags$script(src = "https://unpkg.com/simplebar@latest/dist/simplebar.min.js")
    ),
    reactable(iris, height = "300px", elementId = "testtable"),
    htmlwidgets::onStaticRenderComplete("
      new SimpleBar(document.querySelector('#testtable .rt-tbody'))
    ")
  )
)

But yeah, it seems like SimpleBar doesn't work on the tbody for whatever reason. I see that the DOM is modified with the simplebar elements, but nothing else seems to change. Very odd, and I'm not sure why it wouldn't work, maybe a SimpleBar issue or limitation.