How can I format the last row of a table?

I can format the first row, I need to get the last row also

#set table(
	inset: (x:0.5em,y:.4em),
	stroke:_gridlines,
	fill: (x, y) => if y==0 { rgb(_shadecolor) },
)
1 Like

Hello @mgholam,

Unfortunately, I don’t seem to have found a programatic way to shade the last row of your table. You should be able to using a variable, and changing n-rows appropriately for each table.

#let n-rows = 3
#set table(
	inset: (x:0.5em,y:.4em),
	stroke:_gridlines,
	fill: (x, y) => if y==0 or y == n-rows { rgb(_shadecolor) },
)

don’t hesitate to provide a full minimal example the next time you ask a question, as stated in How to post in the Questions category.

Thanks!
That is what I did, set a variable and checked against that.

1 Like

I had proposed one potential way around this in this issue: Resolve further fields in `grid` and `table` show rules · Issue #3697 · typst/typst · GitHub

Though I’m not fully sure whether that is the best way forward. Frankly I agree that this is quite an annoying limitation. I hope we can find a good design that solves it.

If you put you cells in an array, you can automatically compute the number of rows according to the number of columns, like so:

#let n-cols = 3
#let cells = ([A], [B], [C], [D], [E], [F], [G])
#let n-rows = calc.ceil(cells.len() / n-cols)
#table(
  columns: n-cols,
  fill: (_, y) => if y == n-rows - 1 {
    gray.lighten(50%)
  } else {
    none
  },
  ..cells
)

If this is something you need in multiple places, you could even wrap it in a function, for example:

#let my-table(cells, ..args) = {
  let columns = args.named().at("columns", default: 1)
  let n-cols = if type(columns) == int {
    columns
  } else {
    columns.len()
  }
  let n-rows = calc.ceil(cells.len() / n-cols)
  table(
    columns: columns,
    ..args,
    fill: (_, y) => if y == n-rows - 1 {
      gray.lighten(50%)
    } else {
      none
    },
    ..cells
  )
}

Taking @LordBaryhobal’s solution one step further so that it handles cells that span columns and rows:

#let my-table(cells, ..args) = {
  let columns = args.named().at("columns", default: 1)
  let n-cols = if type(columns) == int {
    columns
  } else {
    columns.len()
  }
  let n-cells = cells.fold(
    0,
    (curVal, cell) => curVal + cell.at("colspan", default: 1) * cell.at("rowspan", default: 1)
  )
  let n-rows = calc.ceil(n-cells / n-cols)
  table(
    columns: columns,
    ..args,
    fill: (_, y) => if y == n-rows - 1 {
      gray.lighten(50%)
    } else {
      none
    },
    ..cells
  )
}

Then it can be used as before:

#my-table(
  (
    table.cell(colspan: 2, [A]), table.cell(rowspan: 2, [B]),
    [C], [D],
    table.cell(colspan: 3, [E]),
    [F]
  ),
  columns: 3
)

image

The change was to not trust the length of the cells array, but to use fold to process each one, checking for colspan and rowspan, getting the total number of spaces taken up by the given cells.

4 Likes

Now you’re thinking in tables :wink:
Good job!

You can customize some cell properties based on the table size like this :

#show table: t => {
  let width = t.columns.len()
  let height = t.children.len() / width

  show table.cell.where(y: height - 1): strong
  show table.cell.where(x: width - 1): c => {
    set align(right)
    c
  }

  t
}

Unfortunately I couldn’t make it work with the fill property.