Documentation

Lists & Tables

Ordered, Unordered, and Data Tables.

For structured, repeating data, PaperCast provides List and Table widgets.

List Nodes

PaperCast supports both UnorderedList (bullet points) and OrderedList (numbered items).

Usage

A List node acts as a container. You drop other widgets (like Text nodes) inside the List, and PaperCast will automatically wrap them in <li> tags during rendering.

  • Unordered List: Renders with list-style-type: disc and 20px of left padding.
  • Ordered List: Renders with list-style-type: decimal and 20px of left padding.

You can use the Bind tab on a List node with the Repeat mode to dynamically generate bullet points based on an array of data in your JSON payload!

{
  "type": "unordered-list",
  "bind": {
    "path": "invoice.items",
    "mode": "repeat",
    "itemAlias": "item"
  },
  "children": [
    {
      "type": "text",
      "bind": { "path": "item.name" }
    }
  ]
}

Table Node

The Table widget is one of the most powerful and complex elements in PaperCast. It is designed to handle tabular data (like an invoice item breakdown) that may span across multiple pages.

Data Binding

Unlike other containers where you map a repeating binding to the container itself, the Table node requires you to define a bindPath pointing to an array of objects.

Column Configuration

In the Props tab of a Table widget, you can configure the columns:

  • Header Text: The string to display at the top of the column.
  • Bind Path: The key in your data object to map to this column (e.g., item.price).
  • Width Px: A fixed width in pixels for the column.
  • Flex: Alternatively, you can use flex ratios (e.g., Column 1 has flex 2, Column 2 has flex 1) to distribute the table width proportionally.
  • Align: Align the text in the column (left, center, right).

Pagination Behavior

Tables can be massive. When a table reaches the bottom of a page, the Pagination Engine intelligently slices the rows.

By default, the Table Header will repeat at the top of the table on the next page so users don't lose context!

If you do not want the header to repeat when a table splits across pages, you can set hideHeaderOnSplit: true in the Table's properties.

{
  "type": "table",
  "bind": { "path": "invoice.items" },
  "props": {
    "hideHeaderOnSplit": false,
    "columns": [
      {
        "headerText": "Description",
        "bindPath": "desc",
        "flex": 2
      },
      {
        "headerText": "Total",
        "bindPath": "total",
        "flex": 1,
        "align": "right"
      }
    ]
  }
}

Static Table Rows

Tables don't always just render mapped array data. You can configure Static Rows at the top, middle, or bottom of the table using the headerRows, bodyRows, and footerRows properties.

These static rows allow for advanced layouts like:

  • multi-level headers with manual colSpan and rowSpan.
  • custom summary rows at the bottom of your data (e.g. Subtotal, Tax, Grand Total).
  • embedding arbitrary Widgets (like a barcode or an image) inside a table cell.

Dynamic Cell Merging (mergeBy)

For complex data grouping (e.g., grouping line items by Category), PaperCast offers a powerful Value Grouping feature via the mergeBy array on a TableColumnConfig.

By specifying an array of data paths in mergeBy (e.g., ["category.id", "category.name"]), the Pagination Engine will look ahead and group identical contiguous values, generating a rowSpan dynamically.

The Pagination Engine ensures that dynamic groups are never severed by a page break. If a grouped block is too large for the current page, it will gracefully slice the array so that the group span carries over accurately to the next page!