gt icon indicating copy to clipboard operation
gt copied to clipboard

Align tables from `gt_split()`

Open AlbertRapp opened this issue 2 years ago • 2 comments

Proposal

It would be cool to put multiple tables from gt_split() next to each other or onto a grid. One way this could be done is to modify the internal print.gt_group function so that it accepts CSS for both the container that contains a single table and the container that contains all tables.

Here are a two drafts I came up with.

Draft 1: Wrap html_tbl_i and the final output into div().

library(gt)
my_print.gt_group <- 
  function (x, css_outer = NULL, css_inner = NULL, ..., view = interactive()) {
    html_tbls <- htmltools::tagList()
    seq_tbls <- seq_len(nrow(x$gt_tbls))
    for (i in seq_tbls) {
        html_tbl_i <- 
          htmltools::div(
            style = css_inner,
            gt:::as.tags.gt_tbl(grp_pull(x, which = i), ...)
          )
        html_tbls <- htmltools::tagList(
          html_tbls, 
          html_tbl_i, 
          if (i != max(seq_tbls)) htmltools::HTML("<br />"))
    }
    htmltools::div(
      style = css_outer,
      html_tbls
    )
}
metro |> 
  dplyr::slice(1:10) |> 
  dplyr::select(name, passengers) |> 
  gt() |> 
  cols_width(name ~ px(250), passengers ~ px(100)) |> 
  gt_split(row_every_n = 5) |> 
  my_print.gt_group(
    css_outer = 'display: flex;',
    css_inner = 'margin-left: auto; margin-right: auto;'
  )

image

For more than two tables one could ad flex-wrap: wrap to the outer div.

metro |> 
  dplyr::slice(1:10) |> 
  dplyr::select(name, passengers) |> 
  gt() |> 
  cols_width(name ~ px(250), passengers ~ px(100)) |> 
  gt_split(row_every_n = 3) |> 
  my_print.gt_group(
    css_outer = 'display: flex; flex-wrap: wrap;',
    css_inner = 'margin-left: auto; margin-right: auto;'
  )

image

Draft 2: Remove line breaks

The previous two approaches do not work if you use display: grid due to the line breaks. Hence, I had to make two changes:

  1. Remove line breaks
  2. Initialize taglist with first table and adjust loop accordingly
grid_print.gt_group <- 
  function (x, css_outer = NULL, css_inner = NULL, ..., view = interactive()) {
    html_tbls <- htmltools::tagList(
      htmltools::div(
        style = css_inner,
        gt:::as.tags.gt_tbl(grp_pull(x, which = 1), ...)
      )
    )
    seq_tbls <- seq_len(nrow(x$gt_tbls))
    for (i in seq_tbls[-1]) {
        html_tbl_i <- 
          htmltools::div(
            style = css_inner,
            gt:::as.tags.gt_tbl(grp_pull(x, which = i), ...)
          )
        html_tbls <- htmltools::tagList(
          html_tbls, 
          html_tbl_i
        )
    }
    htmltools::div(
      style = css_outer,
      html_tbls
    )
}

Then, using display: grid works.

metro |> 
  dplyr::slice(1:10) |> 
  dplyr::select(name, passengers) |> 
  gt() |> 
  cols_width(name ~ px(250), passengers ~ px(100)) |> 
  gt_split(row_every_n = 3) |> 
  grid_print.gt_group(
    css_outer = 'display: grid; grid-template-columns: repeat(2, 1fr);',
  )

image

Remark

In order for this to look good, the width of each column needs to be set with cols_width(). Otherwise, each sub-table will use the minimal necessary width for its data. Thus, if e.g. names are shorter than in the other tables, then that table will not align properly with the other tables. This could maybe be fixed in some iteration but I don't think that it's a big problem to apply cols_width().

AlbertRapp avatar Apr 01 '23 17:04 AlbertRapp

Albert, this is wonderful stuff! This is going to be worked on for the next release. And thank you for adding working code that people can use today!

rich-iannone avatar Apr 01 '23 18:04 rich-iannone

Thanks, I'm glad that my code is helpful ☺️

AlbertRapp avatar Apr 03 '23 10:04 AlbertRapp