A grid is an overlay block whose content lays its children out in rows and columns, so a table, a price list or a badge sheet stays aligned however many rows it has. The layout file reference lists every property.

A grid with gap lines

Put the grid in a layout file. size_from_block makes the grid the block's size:

{ "handle": "items", "type": "overlay", "x": 12, "y": 52, "width": 186, "height": 90,
  "content": { "layout": "grid", "size_from_block": true, "ocols": 3, "orows": 10, "gap_y": 0.3,
               "gap_color_y": "#D0D0D0", "ocol_sizes": "4rem 1rem 1.5rem",
               "orow_sizes": "8mm 8mm 8mm 8mm 8mm 8mm 8mm 8mm 8mm 8mm",
               "placements": [] } }

Fill it from a script

After the layout is placed, write the cells with setCells(grid, startIndex, cells) (slots are 1-based). cell_sizing: "stretch" fills the cell. addBlock into a grid is refused; use setCells.

overlay("overlay.canvas");
var cells = [];
inputs.rows.forEach(function (row) {
  cells.push({ type: "text", width: 60, height: 6, text: row.name,
               valign: "center", cell_sizing: "stretch" });
  cells.push({ type: "text", width: 30, height: 6, text: "$" + row.price,
               align: "right", valign: "center", cell_sizing: "stretch" });
});
setCells("items", 1, cells);

Cells fill left to right, then down. A grid holds orows x ocols children; any beyond that are dropped. Only rows that have cells get gap lines, so unused rows stay empty. getCount("items") is the number of slots now, and getCells("items", 1, 3) reads cells back. A null cell clears its slot. orderCells, orderOrows and orderOcols ((grid, start, end, direction)) reorder cells or whole lanes by whether they hold data: "asc" puts those without data first, "desc" packs data to the front.

Fill origin and direction

fill_origin is the corner cell 1 sits in (top_left, top_right, bottom_left, bottom_right); fill_axis is the way cells flow from it (horizontal or vertical). orows is the number of lanes and ocols the cells along the flow in each lane. With the default horizontal fill these are the rows and columns; with a vertical fill they swap. ocol_sizes and orow_sizes are listed from the fill origin.

A cell can name its own place with orow and ocol (1-based); the layout file reference lists the other cell keys. There is no column span; nest an overlay grid inside a cell instead.

Shrink and grow

Empty trailing lanes or columns can collapse, and the grid can size itself from its cells: leave its width and height out and each is the total of its tracks (a track is its largest cell), plus the gaps and the grid's own frame. Turn on size_from_overlay on the overlay block and the block follows that size. getBlockBounds and getBlockInfo report the size a block renders at right now, so a script can place content directly under a contracting grid. getBlockInfo also returns declared_width / declared_height when they differ from the authored size.

  • min_orows, min_ocols - lanes / columns always kept; trailing ones with no data collapse.
  • overflow_orows - extra lanes past orows, existing only while they hold data.

A cell has data when its has_data is true, or when it holds a visible, non-blank block.

Repeat a template per record

To build one row per entry of a repeating group, set repeating_group_id and write the placements as a template using {inputs.field}:

"content": { "layout": "grid", "ocols": 2, "orows": 10, "repeating_group_id": "options",
  "placements": [
    { "type": "text", "text": "{inputs.description}", "cell_sizing": "stretch" },
    { "type": "text", "text": "{inputs.price}", "align": "right", "cell_sizing": "stretch" }
  ] }

No script call is needed: when process.js calls overlay() for the file, the template repeats for each record (using the inputs as they are at that call). To add a totals row after the records, use setCells("items", getCount("items") + 1, [...]). Record N's blocks are addressed by position, e.g. items.N.2 (or by a handle given to the template block).