This is a proof of concept of #setrow(prop)
/ #setcol(prop)
styling for tables, similar to what exists in the tabularray latex package.
A #setrow(style)
call inside a table cell becomes the equvialent to the show rule show table.cell.where(..): style
for the particular row the directive was placed.
The proof of concept also has a special case where #setrow(c)
sets the fill color of the row (or column) if c
is a color. This is done by changing the table fill
field, since we can’t change cell fill color using a style rule on table.cell
itself.
Style
/// Take element and replace it with a new version of itself with arguments ..args applied
/// pos: which named fields to consider as positional, for replacement
/// if pos is auto, any body or children fields are considered positional
#let replace-elt(element, pos: auto, ..args) = {
let fields = element.fields()
if pos == auto { pos = fields.keys().filter(f => f in ("body", "children")) }
let (fields, posargs) = pos.fold((fields, ()), ((fields, args), elt) => {
let value = fields.remove(elt)
(fields, (..args, ..if type(value) == array { value } else { (value, )}))
})
(element.func())(..fields, ..posargs, ..args)
}
#assert.eq(
replace-elt(strong[x], delta: 1),
strong(delta: 1, [x])
)
#assert.eq(
replace-elt(table(stroke: 2pt)[x][y], columns: 2, [z]),
table(stroke: 2pt, columns: 2, [x], [y], [z]),
)
#let prefix = "__setcol_"
#let tablenr = counter(prefix + "tablenr")
#let setrowlab = label(prefix + "setrow")
#let setcollab = label(prefix + "setcol")
#let processedlab = label(prefix + "processed")
#let procrules(tab) = {
if tab.at("label", default: none) == processedlab {
return tab
}
tablenr.step()
context {
let nr = str(tablenr.get().first())
let rowrules = state(prefix + "rowrules:" + nr, ())
let colrules = state(prefix + "colrules:" + nr, ())
let rowv = rowrules.final()
let colv = colrules.final()
let show-rules = (
rowv.map(((y, func)) => (arguments(y: y), func))
+ colv.map(((x, func)) => (arguments(x: x), func)))
let fill-rules = show-rules.filter(((_, f)) => type(f) == std.color)
let show-rules = show-rules.filter(((_, f)) => type(f) != std.color)
let fill = (x, y) => {
for (rule, color) in fill-rules {
if rule.at("x", default: none) == x { return color }
if rule.at("y", default: none) == y { return color }
}
if tab.fill != none {
if type(tab.fill) == function { (tab.fill)(x, y) } else { tab.fill }
}
}
show: doc => {
let doc = doc
for (args, func) in show-rules {
doc = {
show std.table.cell.where(..args): func
doc
}
}
doc
}
show std.table.cell: it => {
if it.x == auto or it.y == auto { return it }
show selector.and(metadata, setrowlab): meta => {
rowrules.update(arr => {
(..arr, (it.y, meta.value))
})
meta
}
show selector.and(metadata, setcollab): meta => {
colrules.update(arr => {
(..arr, (it.x, meta.value))
})
meta
}
it
}
[#replace-elt(tab, fill: fill)#processedlab]
}
}
Document
/// Style current table cell row using the given function.
/// if the argument is a color, set the table fill color for this row.
#let setrow(func) = {
[#metadata(func)#setrowlab]
}
/// Style current table cell column using the given function.
/// if the argument is a color, set the table fill color for this column.
#let setcol(func) = {
[#metadata(func)#setcollab]
}
#show table: procrules
#table(
columns: 2,
[#setrow(text.with(green))#setcol(strong) A], [B],
[#setrow(text.with(purple))#setcol(yellow) C], [#setcol(emph)#setcol(aqua) D],
)
#import "@preview/rowmantic:0.3.0": rowtable, expandcell
#rowtable(
separator: ",",
fill: (x, y) => {
yellow.desaturate(50%)
},
[ A #setcol(red),
B #setcol(blue)#setcol(text.with(white)),
C,
D #setcol(yellow)#setrow(strong)#setrow(text.with(1.44em))],
[E, F, G, H],
[I, J, K, L],
[M, N, O, P],
[Q, R, S, T #setrow(lower)],
[U, V, X, Y],
[#expandcell[Z #setrow(aqua)#setrow(align.with(center))], #table.cell(fill: black)[]],
)
The example uses rowmantic/rowtable for creating the table but it is not related or necessary for the setrow/setcol proof of concept at all.
Once we have this proof of concept going we can ask - is this a good idea? Is this a good interface?
I don’t think it’s ideal but it has some good things going for it.