-2

This is more of an architectural issue, but basically, I have a server-side React app that renders a bunch of charts and tables with page breaks in-between, so that a puppeteer instance can open the page then print, and send that printed report back to the user in another app.

I need to be able to take some data that is normally rendered into a table format on this app, but make it printable so that the data extends as far as possible before a page break is required, then renders a new table past the page break (so it appears on a new page when printing), and continues until all of the data is rendered into the report. Essentially, I need pagination on a table, without the user interaction that pagination usually comes with.

The thing I'm struggling with is that the length of the data is dynamic, and so are the widths and heights of the rows.

Any suggestions on how to tackle this? The only thing I can think of so far is to basically hide the table, and measure the height of it after every row is attached, and compare that to the max height (the height of a standard letter size in pixels), and if it exceeds it, remove the row, add a page break, then start a new table.

Thanks in advance.

EDIT:

FYI, The solution mentioned here doesn't apply: How to apply CSS page-break to print a table with lots of rows?

This needs to be an entirely new table because I have custom headers and footers that are going above and below it (showing metadata like the name of the chart, how many rows are shown out of how many total, etc.), so it can't just be one continuous table that's split.

Here's a codepen with a shell of what I'm trying to do. If you open in debug view, and print it, you'll see in the print preview that the table is split up across two pages, but the footer I created will only be on the second page (where it needs to be on both pages, after the table). Additionally, the footer needs to display the dynamic count of rows that were able to fit on the page, so it can't be a static part of the table as a tfoot element. https://codepen.io/nicholaswilson/pen/GRWNzMa

So I'm trying to figure out now if I can mount the table to the DOM, but hide it, and calculate the height as I add rows to it so I can try my original method above. But I'm also open to other suggestions.

the-nick-wilson
  • 566
  • 4
  • 18
  • Does this answer your question? [How to apply CSS page-break to print a table with lots of rows?](https://stackoverflow.com/questions/8712677/how-to-apply-css-page-break-to-print-a-table-with-lots-of-rows) – Chris W. May 19 '21 at 18:46
  • @ChrisW. - no, unfortunately. This needs to be an entirely new table because I have custom headers and footers that are going above and below it (showing metadata like the name of the chart, how many rows are shown out of how many total, etc.), so it can't just be one continuous table that's split. Unless I'm missing something here. – the-nick-wilson May 19 '21 at 18:48
  • I assume the downvote was because there's no reproducible example/effort shown and is looking for suggestion. – Chris W. May 19 '21 at 18:48
  • I mean, I guess I can throw together something based on my solution (which I explained here), but regardless, I am indeed looking for other possible suggestions because mine might not be the most optimal. – the-nick-wilson May 19 '21 at 18:50
  • Yea I hear ya, but with stuff that's pretty instance specific most suggestions like the one I made would be just one-off guesses without seeing the mechanics of the problem specifically to troubleshoot. Sounds like you'll probably need to just designate [page-break](https://css-tricks.com/almanac/properties/p/page-break/) areas manually and tweak until you get what you want. – Chris W. May 19 '21 at 18:54
  • That's fair. Thanks @ChrisW. - I'll post a CodePen in a bit to clarify the mechanics. – the-nick-wilson May 19 '21 at 18:59
  • @ChrisW. - added a codepen. Hopefully this will help a bit to illustrate. – the-nick-wilson May 19 '21 at 23:11
  • OoOh ok, it sounds more like you're just after the [tfoot](https://www.w3schools.com/tags/tag_tfoot.asp) element that's made for this kind of scenario like `thead` is. Are you just looking for [something like this](https://codepen.io/chris-w/pen/VwpmRjZ)? Or if there's a specific reason to have the separate div then you can get kind of the same result with like `@print { .footer {position: fixed; bottom: 0; } }` to repeat on pages with some tweaking if I'm understanding now? – Chris W. May 20 '21 at 00:34
  • So that's definitely close. But the problem w/ using `tfoot` is that the footer is static in that case. But I need to show a running count of records on that page, out of how many total. So for example, on the first page, it would say "Showing 1-25 of 27 results", but the next the footer would say "Showing 26-27 of 27 results." This is where it throws a wrench into using `tfoot` as intended. The content of that footer needs to be dynamic, per page - but that's not the element's intent as far as I know, unfortunately. – the-nick-wilson May 20 '21 at 02:43
  • And it's the same sort of issue using a separate footer div - I need to be able to track those breaks in the table so I can have the correct numbers in the footer. I can't find those breaks programmatically as far as I can tell. – the-nick-wilson May 20 '21 at 02:45

1 Answers1

0

Alright, I think I got it. Still needs some tweaking (and there are probably more performant ways to do it) but this is my concept: https://codepen.io/nicholaswilson/pen/abJpLYE. Currently I'm splitting the tables after they've exceeded the height, but I'll be fixing that later. The concept is here.

Basically, the idea is to build a 4D array to represent the instances of tables that need to be rendered. Then in componentDidMount() and componentDidUpdate(), I can add new tables to the state as needed:

componentDidMount() {
const { tableData } = this.props;

if (this.state.currentRowIndex === 0) {
  // just starting out
  this.setState((state, props) => {
    const tables = state.tables;
    tables.push([tableData.data[state.currentRowIndex]]); // push first new table and first row
    return {
      tables,
      currentRowIndex: state.currentRowIndex + 1,
      currentTableIndex: 0
    };
  });
}

}

componentDidUpdate() {
    const { tableData } = this.props;
    if (this.state.currentRowIndex < tableData.data.length) {
      this.setState((state, props) => {
        const tables = state.tables;
        const currentTableHeight = this.tableRefs[this.state.currentTableIndex]
          .clientHeight;
        console.log(
          `Table ${this.state.currentTableIndex} height: ${currentTableHeight}`
        );
        if (currentTableHeight > MAX_TABLE_HEIGHT_IN_PIXELS) {
          // push a new table instead based on this condition
          tables.push([tableData.data[state.currentRowIndex]]);
          return {
            tables,
            currentRowIndex: state.currentRowIndex + 1,
            currentTableIndex: state.currentTableIndex + 1
          };
        } else {
          tables[state.currentTableIndex].push(
            tableData.data[state.currentRowIndex]
          ); // push new row to existing table
          return {
            tables,
            currentRowIndex: state.currentRowIndex + 1,
            currentTableIndex: state.currentTableIndex
          };
        }
      });
    }
  }

See the codepen for the rest of the implementation.

the-nick-wilson
  • 566
  • 4
  • 18