#Tables

6788 messages ยท Page 7 of 7 (latest)

wary knot
#

what's more important there is showing that a cell is spanning two columns

#

but i mean, the most important takeaway here is that we need an example showing how to remove vertical lines

upbeat pine
#

wait, so h/vline have the highest priority?

wary knot
#

yes

upbeat pine
#

I thought they have the medium priority.

wary knot
#

๐Ÿ‘†

upbeat pine
#

Yeah, but what was the argument for this again?

wary knot
#

you cant add hlines / vlines in templates

upbeat pine
#

which templates?

wary knot
#

#set table(...)

#

and #set table.cell(...)

#

so it's natural that the thing which the user has to explicitly specify at call site will have more priority over things which can be overridden globally

upbeat pine
#

So you are saying that #set table.hline(...) doesn't exist?

wary knot
#

it does

#

but you have to place the hline for it to have any effect

#

if your table has no hlines that doesnt change anything

#

whereas #set table.cell(...) and #set table(...) will affect all tables regardless of what you use

upbeat pine
#

It would be cool if you can disable only horizontal lines with the set rule. Which means that the table's rules are affecting vlines and hlines.

#

I even think now that it is a better approach.

wary knot
#

but you can

#

the thing is that vlines and hlines changed their semantics from what we had thought initially

#

initially we had defined vline and hline to be any vertical or horizontal line in the table

#

so indeed hline and vline would have low priority because they're just a basic thing

#

but after our call we decided that hlines and vlines are only user overrides

#

the "basic lines" in the table are really just cell strokes

#

so #table(stroke: ...) sets cell strokes

upbeat pine
#

oh, right.

wary knot
#

and so if you want to disable horizontal lines , you can write #table(stroke: (y: none)) for instance

upbeat pine
#

...

wary knot
#

it disables the line above and below the cell

#

equivalent to (top: none, bottom: none)

upbeat pine
#

I don't know what to say. I'm just impressed.

#

It's like writing set rule for v/hlines, but in the same place, just chaning x/y.

#

truly genius. It is much more convenient, but you need to write table/stroke/y/...,x/...

#

But are those lines override the cell's stroke or create new lines on top?

#

I think this also was discussed...

wary knot
#

if it doesnt define its own

upbeat pine
#

if cell doesn't define its own stroke?

wary knot
#

yeah

upbeat pine
#

what does "define" mean?

wary knot
#

table.cell(stroke: red)[a]

upbeat pine
#

define define

wary knot
#

think of #grid(stroke: blue, stuff) as

#[ #set grid.cell(stroke: blue) #grid(...) ]

upbeat pine
wary knot
#

no

#

cuz theres no hline

#

unless you place one

#

then it will have higher priority

upbeat pine
wary knot
#

if you place one then the hline has higher priority

upbeat pine
#

so hline overrides cell's stroke if cell doesn't override its own stroke. But if the cell does override its own stroke, then hline will still have a higher priority.

wary knot
#

no

#

sorry i misunderstood what you had said

upbeat pine
#

that's how I interpreted your words.

wary knot
#

i meant that #grid(stroke: ...) sets the cell's stroke field

#

unless you set it manually, then it doesnt

#

and regardless of that

#

grid.hline and grid.vline will appear on top

upbeat pine
#
#table(
  columns: 3,
  [], [], table.cell(stroke: green)[hey],
  table.hline(stroke: red),
  table.cell(stroke: green)[hey], [], [],
)
#

Indeed...

#

So, I'm guessing that for simple tables if you don't want to override, you just add start/end. And for complex tables you are on your own, probably have a complex show rule for everything as well.

#

I don't usually change hlines, but it is something that I will need to experience myself to know if it's good or bad.

#

I feel like begin/end is a more iconic duo and h/vline should use it instead of start/end. Because normally it's start/stop.

wary knot
#

it's analogous to the line parameters

upbeat pine
#

indeed

#

https://staging.typst.app/docs/reference/model/table/#definitions-hline-stroke:

The line's stroke.

Specifying none interrupts previous hlines placed across this line's range, but does not affect per-cell stroke or vlines.
What does interrupts previous hlines mean? It does affect per-cell stroke:

#table(
  columns: 3,
  [], [], table.cell(stroke: green)[hey],
  table.hline(stroke: none),
  table.cell(stroke: green)[hey], [], [],
)
wary knot
#

oh yea good catch

#

laurenz asked me to change that i think but i forgot to update the docs

upbeat pine
#

which metadata?

wary knot
#

i assume that was intentionally unspecified as it wasnt implemented yet

#

but in general, something that indicates what the header of your table is

upbeat pine
#

And what difference will it make? For clarity?

wary knot
#

for accessibility

upbeat pine
#

I know that

wary knot
#

so maybe a screen reader will say "table header"

#

well we cant really reason about it as it doesnt exist

#

it's just a vague suggestion to use it whenever something of the sort is added

#

I assume martin had PDF-A in mind when he wrote that

#

or something like that

upbeat pine
#

it isn't implemented, but it must exist already, otherwise this whole vague stuff is useless.

wary knot
#

why is it useless?

#

it's just encouraging people to future-proof their stuff

#

pretty normal

upbeat pine
#

if the thing doesn't exist, then there is no point in planning to add it.

wary knot
#

why not?

upbeat pine
#

Because we don't know what will happen in the future. otherwise we need to predict 100 stuff that might be created and then added in the typst.

wary knot
#

that sounds like a strawman though

#

it's not saying to prepare yourself against 100 unpredictable stuff

#

it's saying to prepare yourself against one specific thing which is planned and will be added

upbeat pine
#

I thought there is some kind of standard, but if it's nothing, then our standards are bad, this should've been existed for a long time.

wary knot
#

there is

#

i mean there are PDF standards

#

it's probably just gonna use one of them

#

i just cant tell you cuz i dont speak PDF

#

so the info i have is very limited

wary knot
#

now that they do, we can begin to consider implementing this

#

anyway

upbeat pine
wary knot
#

i cant give much more info

wary knot
#

also for example, when html export becomes a thing

#

a table header would be exported as <thead>

upbeat pine
wary knot
#

with "it" i meant the typst implementation

upbeat pine
#

how fragile the language is sometimes...

#

telepathy when?

#

do you really can't combine them?

wary knot
#

#show table.cell.where(x: 0).or(table.cell.where(y: 0)): strong

#

that's the only way

upbeat pine
#

sad

wary knot
#

well, or

#show table.cell: it => {
  if it.x == 0 or it.y == 0 {
     strong(it)
  } else {
     it
  }
}
upbeat pine
#

I guess the only way is to add new syntax where(x: 0 or y: 0) or make where(x: 0).or(where(y: 0)).

#

so basically tell typst to use where for the same thing as in the first time.

#

Wait, if you import cell, do you do #show cell.where().or(cell.where())?

wary knot
#

sure

upbeat pine
#

well, this is much better.

#

No other table cells may be placed after the footer.
even if you specify the cell's position as above the footer?

wary knot
#

in that case it's fine

upbeat pine
#

mMm

upbeat pine
wary knot
upbeat pine
upbeat pine
#

I wanted to move out euro to make the table extra cool, but apparently I can't even do that properly:

// #set table.cell(stroke: green) // works
#set table.cell(inset: (right: 0pt)) // works
#show table.cell: it => {
  // set table.cell(stroke: green) // doesn't work
  if it.x > 0 and it.y > 0 {
    set table.cell(inset: (right: 0pt)) // doesn't work
    // set table.cell(inset: 0pt) // doesn't work
    align(it.align, box(it) + box(inset: it.inset + (left: 0pt, right: 5pt))[#sym.spaceโ‚ฌ])
  } else { it }
}

#table(
  fill: (col, _) =>
    if calc.odd(col) { luma(240) }
    else { white },
  align: (col, row) =>
    if row == 0 { center }
    else if col == 0 { left }
    else { right },
  columns: 4,
  [], [*Q1*], [*Q2*], [*Q3*],
  [Revenue:], [1000], [2000], [3000],
  [Expenses:], [500], [1000], [1500],
  [Profit:], [500], [1000], [1500],
)
#

Why is the local set rule doesn't work? it is illegal, isn't it?

#

Also normal space is ignored there, so instead of becoming extra cool, it becomes extra ugly.

wary knot
upbeat pine
#

It doesn't answer the first question.

#

moreover, I need to know how to overcome this limitation to achieve the goal, which is pretty reasonable, but hacky nevertheless.

#

If I was able to modify the it.body, then it would all be fine, but I can't.

wary knot
# upbeat pine moreover, I need to know how to overcome this limitation to achieve the goal, wh...
#let eurotable(columns: auto, euro-cols: none, table-func: table, ..args) = {
  if type(euro-cols) == int {
    euro-cols = (euro-cols,)
  }
  assert(euro-cols == none or type(euro-cols) == array and euro-cols.all(x => type(x) == int))
  let cells = args.pos()
  let opts = args.named()
  let c = if type(columns) == int {
    calc.max(columns, 1)
  } else if type(columns) == array {
    calc.max(columns.len(), 1)
  } else {
    1
  }

  cells = cells.enumerate().map(((i, cell)) => {
    let cell-func = if type(c) == content { c.func() } else { none }
    let cell-x = if cell-func == table-func.cell and cell.x != auto { cell.x } else { calc.rem(i, c) }
    let cell-y = if cell-func == table-func.cell and cell.y != auto { cell.y } else { calc.floor(i / c) }
    if euro-cols != none and cell-func not in (table-func.footer, table-func.header, table-func.hline, table-func.vline) and cell-x in euro-cols and cell-y > 0 {
      let fields
      let body
      let label
      let cell = if cell-func == table-func.cell {
        fields = cell.fields()
        body = if "body" in fields { fields.remove("body") }
        label = if "label" in fields { fields.remove("label") }
      } else {
        fields = (:)
        body = cell
        label = none
      }
      [#table-func.cell([#body โ‚ฌ], ..fields) #label]
    } else {
      cell
    }
  })

  table-func(..cells, ..opts, columns: columns)
}

#eurotable(
  euro-cols: (1, 3),
  fill: (col, _) =>
    if calc.odd(col) { luma(240) }
    else { white },
  align: (col, row) =>
    if row == 0 { center }
    else if col == 0 { left }
    else { right },
  columns: 4,
  [], [*Q1*], [*Q2*], [*Q3*],
  [Revenue:], [1000], [2000], [3000],
  [Expenses:], [500], [1000], [1500],
  [Profit:], [500], [1000], [1500],
)
upbeat pine
#

you can't mutate read-only variables.

wary knot
upbeat pine
wary knot
#

What's the problem

#

?

upbeat pine
#

missing let euro-cols = euro-cols

wary knot
#

does it error if you run the code?

upbeat pine
#

if you change the type of the argument when you call the function

wary knot
#

Does it error then?

upbeat pine
#

Pretty sure it would.

wary knot
#

There's only one way to find out

#

Haha

#

But in principle it shouldn't

#

You aren't attempting to mutate an external variable

#

Euro-cols is an internal variable

#

You would have an error if it were an array and you tried to append to it

#

But here we're replacing its contents entirely

upbeat pine
#

hmm, strange. did all my previous errors were due to a different use case...

upbeat pine
#

So you basically reconstructed the table cell from its pieces into a new table cell, which I wanted to do, but in show rule this will fail due to recursion.

#

I assume, since you've made a big-ass function instead, there is no way to do it properly with a single show rule.

wary knot
#

i think one possible idea to make table styling easier could be to synthesize table properties

#

e.g. replace the (auto, auto) columns by (1pt, 2pt)

#

even table.children could theoretically be synthesized

#

but in practice i feel like that would be harder to get right

upbeat pine
#

is this related to the not working show set thing?

wary knot
#

no

#

it's just a feature idea

#

being able to show table and have access to the final sizes of columns and rows

upbeat pine
#

And now you can't?

wary knot
#

no, table fields in show rules will be almost exactly as you specified them

#

columns will be changed from 2 to (auto, auto) for example

#

but not much more than that

upbeat pine
#

yeah, shame

wary knot
#

also

#

i found a good use case for a fit parameter for cells, to have them not affect auto rows

#

basically being able to have some line as large as the adjacent content, like block(stroke: ...) but with more flexibility

#

so you'd be able to have anything as large as adjacent content

#

while it would be very quick to implement that, theres of course no time to add it to this release, so we can discuss for 0.12

#

personally i think it'd bring value, much like fit-spans in tablex, but I'm open to hearing other opinions about such a feature

wary knot
#

looks cool

#

basically easier way to make stuff have the same height as other stuff

#

without having to measure()

#

or same width

#

well the grid does measure internally but you dont need to know that

wary knot
#

anyway but that'll be for 0.12 so i don't wanna give it too much importance
I might open a draft PR later just in case

wary knot
#

Okay

#

@robust viper i found a bug (or, at least, inconsistency) in tables

#

TL;DR:
#table(
rows: 2em,
block(width: 2em, height: 100%, fill: red)
)

Produces a small block,

#

But wrapping it in a rowspan: 2 table cell causes it to go all the way to the end of the page

robust viper
#

Luckily not a regression though

wary knot
#

That's true, though it's worth considering

#

Basically I'm just using the region full from the first region the rowspan appears in, ideally i should change that :p

robust viper
#

the rowspan behaviour seems more correct

#

ah no

#

nevermind

#

didn't see the rows: 2em

wary knot
#

yeah

#

In theory it's a quick fix, we just need to determine the criteria for using or not the first region full

#

Maybe spans auto row = yes, doesn't = no?

robust viper
#

seems sensible

wary knot
#

I think unbreakable cell = no as well for consistency

#

As an unfortunate consequence of measuring with infinite height, an unbreakable auto row won't pick up 100% properly

#

So yeah

#

Anyway

#

I'll take a look at this in a few hours, once I'm at my PC

#

Should just be a simple "if" in layout_rowspan

bold bluff
#

I really like the tables.

#let spin(word,origin,length) = {
  return (
    table.cell(colspan:length,x:origin.x,y:origin.y)[#word],
    table.cell(rowspan:length,x:origin.x+length,y:origin.y,rotate(90deg,reflow:true)[#word]),
    table.cell(colspan:length,x:origin.x + 1,y:origin.y+length,align:right,rotate(180deg,reflow:true)[#word]),
    table.cell(rowspan:length,x:origin.x,y:origin.y + 1,align:bottom,rotate(270deg,reflow:true)[#word])
  )
}

#table(columns:8,
  ..spin([TABLES],(x:0,y:0),7),
  ..spin([TABLES],(x:1,y:1),5),
  ..spin([TABLES],(x:2,y:2),3),
  table.cell(rowspan: 2,colspan: 2,x:3,y:3,align: center+horizon)[#emoji.star]
)
pulsar sedgeBOT
bold bluff
#

They work good even when nesting.

upbeat pine
#

I don't understand what is happening, but this is impressive.

wary knot
#

I'll be working on this soon, just got home

#

It's kinda funny how many bugs I find out by just reflecting about things lol

wary knot
#

And then i thought well i wonder if there would be any problems in implementing this on rowspans

#

And then i thought ... Wait ... Lol

#

Anyway , but even then, I'm happy that we haven't rlly been facing any major bugs

#

Even though lots of complex stuff has been added

#

So yay

wary knot
bold bluff
#

so i found a weird thing here i get an error if i have less than 15 columns:

#table(columns:15,
..range(0,10).map(y => {
range(0,10).map(x=>{if calc.rem(x,2) == calc.rem(y,2) {table.cell(rowspan:2,x:x,y:y,[#x])}else {} })
}).flatten()
)
wary knot
#

and whats that error?

bold bluff
#

attempted to place a second cell at column 9 row 1.

#

ehh it changes depending on the number of columns..

wary knot
#

the hint is very helpful

#

i suggest reading it

#

๐Ÿ™‚

#

as explained here

#

you're placing cells with manual positioning followed by one cell with automatic positioning (from the else {}) followed by more cells with manual positioning

#

so the latter cells wont be aware of that one cell with automatic positioning

#

and can potentially try to overlap with it

bold bluff
#

ohh ehh. thats not smart on my part. but it makes sense.

#

does none count as a cell?

wary knot
#

anything in the table is a cell

#

none is converted to [] which is later converted to table.cell[]

#

with all arguments set to their default values

#

solutions:

  1. manual positioning in all cells
  2. place all manually positioned cells before automatically positioned cells
#
  1. use flat_map instead of map
bold bluff
#

flat_map ๐Ÿ™‚

wary knot
#

actually you dont need to flat_map since you already flatten

#

so you can do the following

#

if stuff { (table.cell(...), ) } else { () }

#

array of size 1 vs array of size 0

#

the latter wont create a cell

bold bluff
#

ohhhh. so () is more none than none is. this is very helpful.

wary knot
#

well

#

the thing is that you're using flatten

#

and flatten will replace all arrays by the items inside them

#

recursively

bold bluff
#

yeah i was trying to filter but i was too noob.

wary knot
#

so it will convert ( (...), (...), (...) ) to (..., ..., ...) and then the arrays inside those to their items

#

but () has no items

#

so

#

it's just ignored

timber bronze
#

Hi. I did not follow the table changes very closely, so I assume this is a known limitation, but is there a way to style table headers? For example, I would like to do the following:

#show table.header: set text(weight: "bold")

But this does not work.
I'm sorry if this was already answered, I was unable to find recent information about that.

wary knot
#

Unfortunately won't happen for the next version

upbeat pine
wary knot
#

I'd suggest opening an issue on it

#

Just so i don't forget

#

๐Ÿ™‚

timber bronze
#

I'll do that

wary knot
#

(feel free to ping me there)

wary knot
# upbeat pine

Yeah that'll work for the simplest use cases (though not for arbitrary headers)

timber bronze
# upbeat pine

Yeah I will use this solution in the mean time, but it's not perfect

upbeat pine
#

I saw at least one or more examples of how the table headers were bolded in the docs. I think there are another ones too.

wary knot
#

The table header element can occupy more than one row

#

And you can't rlly know how many rows it occupies without access to internals ATM

#

But I guess you can hack something on #show table like I did for the euro column

#

Either way ideally you'd use #show table.header: ... And it'd just work

#

At least show-set

#

normal show won't work cuz headers don't actually exist

#

They're an illusion of the senses

#

Mere mortals do not understand the complex power living therein

wary knot
#

Ty โค๏ธ

#

Btw @robust viper
Just wanted to mention that the last case in the draft PR i opened is more of a consequence of the workarounds needed to make the rather "exotic" case of breakable rowspans over unbreakable auto rows "work"

Presently, they are measured with infinite height at the region of the auto row, but this isn't really the best thing

So I'll just apply a workaround to make the case listed in the PR work for now (if the unbreakable auto row occurs at the first region of the rowspan, and the rowspan only occupies one region, we also layout the rowspan with infinite height), and in future versions we can study if there's a better way around this weirdness (maybe a way to not have to use infinite full height for the rowspan if it's breakable after all)

#

Don't really wanna delay anything so i think this will be the best way forward, I doubt this will be a common case anyway, just don't want users to think tables are broken :p

wary knot
wary knot
#

actually, can it be a problem to have full height be infinite but remaining height be finite? ๐Ÿค”

#

i guess that's normally weird

#

haha

#

?r ```
#import "@preview/tablex:0.0.8": *

#tablex(
[hi] + block(height: 100%, width: 2em, fill: red)
)

wary knot
#

okay tablex has a similar problem

#

lol

#

the main thing is that 100% and ratios / relative lengths using ratios in general mean different things when measuring and when laying out, within the context of unbreakable auto rows

#

anyway

#

i think we can more or less accept at that unbreakable auto rows - a totally new feature - are more of an experimental thing and just move along

#

i'll undraft the PR soon

azure berry
#

@wary knot i previously had a table without any content to show a caro grid as background for my worksheets. With the new table implementation i need to add content with ..("",) * rows_count * cols_count that the table is shown. Otherwise it won't print anything. Is this intended behavior?

wary knot
#

in principle we didnt make any sort of explicit decision on that matter

#

are you sure it wasnt already like that?

azure berry
#
#let caro(rows, cols:auto) = {
  layout(size => {
    let cols = if( cols == auto ){ int(size.width.cm() / 0.5) } else { cols }
    table(
      columns: (0.5cm,) * cols, 
      rows: (0.5cm,) * rows,
      stroke: 0.3pt + luma(140),
    )
  })
}

this was my previous function and it did work.

#

Now i added the line mentioned above, then it works again

#

Your solution works as well.

#

I guess your solution might be a bit better, performance wise?

wary knot
#

ok i tested in #bot-corner and it seems the previous behavior wasnt very reliable

#

ill see what i can do

azure berry
#

Anyway, i would have expected if i define a certain amount of rows, that they will all be printed, even if no content is available.

#

But the current solution is fine for me as well. ๐Ÿ˜‰

wary knot
#

okay

#

I see what changed that caused this

#

to be honest im not rlly sure what would be the best thing to do here

#

for one, it does make sense for it to respect the rows you specified

#

but for another, specifying more rows than we were given is useful for templates for e.g.

#

being able to specify the sizes of rows if they are actually used

#

still, restoring this behavior would likely be quick to do

#

and probably should be done to avoid breaking changes like in your case

#

although something will certainly change since now you will have actual cells in those rows

#

they wont be truly empty anymore

#

but thats probably an acceptable compromise

#

either way, thanks for letting me know

robust viper
#

@wary knot I checked the PNG files and if a new test is added to a reference at the bottom, most of the binary contents are the same, which should allow git to do a binary delta-coding. As long as the image's width doesn't change and PNG keeps using the same encoding strategy, that means sort of efficient diffs might be possible. This relieves some of the pressure regarding reference images because otherwise just the most recent bug fix PR would have been 240KB added stuff. Still, switching to a hash-based test runner is one of my top priorities for after 0.11.

trail oar
#

But we would need to make it easy to update new ref images and view the difference

robust viper
robust viper
#

I'll open a new forge, give me a minute

midnight heron
#

I am generating a few options for table strokes for the docs guide. It occurred to me that we maybe want to allow set operations on strokes. I got the idea when I talked about the example with Laurenz and he was like I'd do the union between this one and that one. With set operations, packages or the core could export a few strokes which the user could then match to their desire. @wary knot do you think that would be a good feature for the compiler? https://github.com/typst/typst/issues/3636 may also be relevant (cc @runic oasis)

#

For comparison, here are all the table stroke functions I wrote for this demo:

#let af(x, y) = (
  left: if x > 0 { 0pt } else { 1pt },
  right: 1pt,
  top: if y == 0 { 1pt } else { 0pt },
  bottom: 1pt
)
#let nf(x, y) = (
  left: if x > 0 { 1pt },
  right: 0pt,
  top: if y > 0 { 1pt },
  bottom: 0pt
)

#let vertical-vf = (x: 1pt, y: none)
#let vertical-nf(x, _) = if x > 0 { (left: 1pt) }
#let vertical-af(_, y) = (
  x: 1pt,
  top: if y == 0 { 1pt } else { 0pt },
  bottom: 1pt
)
#let horizontal-hf = (y: 1pt, x: none)
#let horizontal-nf(_, y) = (top: if y > 0 { 1pt })
#let horizontal-af(x, _) = (
  y: 1pt,
  left: if x == 0 { 1pt } else { 0pt },
  right: 1pt,
)

#let flb-af(x, y) = (
  left: if x == 0 { 1pt } else {ย 0pt },
  right: 1pt,
  top: if y <= 1 { 1pt } else {ย 0pt },
  bottom: 1pt,
)
#let flb-nf(_, y) = (
  bottom: if y == 0 { 1pt },
)

#let flbh-af(x, y) = (
  left: if x == 0 or y == 0 { 1pt } else {ย 0pt },
  right: 1pt,
  top: if y <= 1 { 1pt } else {ย 0pt },
  bottom: 1pt,
)
#let flbh-nf(x, y) = (
  left: if x > 0 and y == 0 { 1pt } else {ย 0pt },
  bottom: if y == 0 { 1pt },
)

#let flb-h-af(x, y) = (
  left: if x == 0 or y > 0 { 1pt } else {ย 0pt },
  right: 1pt,
  top: if y <= 1 { 1pt } else {ย 0pt },
  bottom: 1pt,
)
#let flb-h-nf(x, y) = (
  left: if x > 0 and y > 0 { 1pt } else {ย 0pt },
  bottom: if y == 0 { 1pt },
)
#let flbh-h-af(x, y) = (
  x: 1pt,
  top: if y < 2 { 1pt } else { 0pt },
  bottom: 1pt,
)
#let flbh-h-nf(x, y) = (
  left: if x > 0 { 1pt } else {ย 0pt },
  bottom: if y == 0 { 1pt },
)
wary knot
#

I'll leave my thoughts in the forge thread later

wary knot
#

As in ..lines

#

I'm not rlly sure what you would use the other set operations for, a use case would be best to evaluate that imo

robust viper
pallid surge
wary knot
#

Lol but yeah i see what you mean

#

Though in principle it sounds a bit intrusive or possibly inconsistent to modify all tables like that

#

Or at least you'd still have to predict the size of the table

#

So it sounds more practical to make table generator functions than to use show / set rules

wary knot
#

Personally I'm in favor of changing that somehow, either changing the example or having something simple enough to be able to include all of the code

robust viper
#

Because the current code is too noisy

wary knot
#

We could use headers to demonstrate colspan

#

And just have a very simple table like that

wary knot
#

Alright, there's one more thing i wanna fix

#

Though it's not urgent

#

So I'll work on it today but it's not a blocker if I don't finish it in time

#

But basically the footer thing where we move to the next row after the latest auto cell isn't really that smart, since it's possible that the next row will also be occupied
Ideally we'd skip until we found a suitable position for the footer

#

i.e.

#table( columns: 2, table.cell(rowspan: 2)[a], table.cell(rowspan: 2)[b], table.footer() )

will error cuz it will try to place the footer at the second row instead of the third

#

But anyway it's not that bad, i think adding a single auto cell to the footer makes the error go away so

#

This mostly just affects lines and stuff really

wary knot
#

the problem wasnt exclusive to empty footers, so it was worth a fix

#

anyway , hopefully this will be the last PR with adjustments and stuff before 0.11

#

:p

#

because there surely are no more bugs!!! ๐Ÿ˜„

exotic leaf
#

Is it desired that the header is affected by row-gutter?

#set page(width: 15cm, height: auto, margin: 1cm)

#table(
  columns: (1fr, 1fr, 1fr),
  stroke: none,
  row-gutter: 0.25cm,
  table.hline(stroke: 2pt),
  table.header([Header 1], [Header 2], [Header 3]),
  table.hline(),
  [Cell 1A],
  [Cell 2A],
  [Cell 2A],
  [Cell 1B],
  [Cell 2B],
  [Cell 2B],
  [Cell 1C],
  [Cell 2C],
  [Cell 2C],
  table.hline(stroke: 2pt),
)
Table with `row-gutter` set to `0.25cm`

#v(1cm)

#table(
  columns: (1fr, 1fr, 1fr),
  stroke: none,
  table.hline(stroke: 2pt),
  table.header([Header 1], [Header 2], [Header 3]),
  table.hline(),
  [Cell 1A],
  [Cell 2A],
  [Cell 2A],
  [Cell 1B],
  [Cell 2B],
  [Cell 2B],
  [Cell 1C],
  [Cell 2C],
  [Cell 2C],
  table.hline(stroke: 2pt),
)
Table with default `row-gutter`
wary knot
#

You can remove the row-gutter below the header by setting it to 0pt

#

row-gutter: (0pt, 0.25cm)

#

Of note, what you have there is a line above the row below the header and not a line in the header itself

#

For that you'd need to use position: bottom on a line above the last row of the header

#

Cuz when there's gutter you have two possible positions for a hline: above and below a row (read the docs on hline.position for more info)

exotic leaf
#

Ok, thanks for the quick response. Is there a possibility to remove the little inset left of the first column?

wary knot
#

try inset: (x: 0pt)

#

On the table

exotic leaf
#

Thank you, this works

midnight heron
#

There sure is a lot to document about tables -- the section on strokes in the WIP table guide alone fills almost 8 screens on my computer!

grand haven
midnight heron
#

Me on my Bloomberg Terminal

wary knot
#

We should probably try to keep it as concise as possible, don't want people to just look at the sheer length of the guide and retreat ๐Ÿ˜…

#

But I think a clarification at the beginning that the guide covers various use cases and they should look for their use case could be helpful

wary knot
#

oh hey

#

just saw the guide was merged

#

I'll take a quick look if y'all don't mind

#

I'll let you know if I have any comments

robust viper
wary knot
#

ah okay

#

no problem

#

I see now that there were indeed a few iterations

#

I'll trust you ๐Ÿซก

robust viper
#

It's also very comprehensive, so it would likely take you a while to review

wary knot
#

fair enough, didnt realize we were so close to release haha

#

anyway , just keep it up then ๐Ÿ‘

#

changing the topic a bit, overall I think the pre-release was very successful

#

we did indeed find and squash some important bugs so that's cool

vernal herald
wary knot
#

yep

robust viper
#

yeah indeed

potent talon
#

The guide is super nice ! I havenโ€™t played with it but it looks like we can set booktabs-like for all document ? If so, that would be a nice example in the doc

wary knot
#

(Just for reference ๐Ÿ™‚ )

potent talon
wary knot
#

Ah

#

There was an example on this in this thread

#

Discord mobile is horrible for threads so I can't search lol

potent talon
#

#let toprule = hlinex(stroke: (thickness: 0.08em))
#let bottomrule = toprule
#let midrule = hlinex(stroke: (thickness: 0.05em))

#figure(
gridx(
columns: 2,
[Catรฉgorie], [Mรฉtrique],
toprule,
[ร‰chantillon], [Concentration ADN],
midrule,
[], [Quantification de la librairie],
bottomrule
))

#

Something like this

wary knot
#

Yeah it's possible

wary knot
#

Regarding lines, you'd have three table.hline() calls, at the top , middle and bottom

#

And the two colspans of 3 would have โ€œstroke: (bottom: 0.5pt + black)โ€

#

The global table stroke would be set to none

potent talon
potent talon
wary knot
#

for the spacing between columns, you can use column-gutter

#

Here the choice of table.hline vs stroke: (top: ...) matters

#

Hlines can cross gutter , while stroke goes around cells, so it can't

potent talon
#

Interesting

upbeat pine
#
...
#let colspan(n, ..args) = table.cell(colspan: n, ..args)
...
#table(
  ...
  table.header(
    [Team member], [Monday], [Tuesday], [Wednesday], [Thursday], [Friday]
  ),
  [Evelyn Archer], colspan(2, ofi), colspan(2, rem), ofi,
  [Lila Montgomery], colspan(5, lea),
  [Nolan Pearce], rem, colspan(2, ofi), rem, ofi,
)

lt was such a great example to import cell or even better: a custom function that will shrink down horizontal space even further.

wary knot
#

i think it's fine

#

^

#

there's an example which mentions importing so i think thats enough

upbeat pine
#

importing still wouldn't be short enough to make 1 row per line.

#

this is redundant.

#

My gut would say "get x n times", so I would've written 5 the last. That's how I write everything, and I think that is how others also write.

wary knot
#

well...

#

im sorry but we're being way too nitpicky at this point :p

#

to be honest im probably not gonna look at these docs again (with the eyes of a reviewer at least) for a good several days

#

let's rejoice , for the release has come forth

#

๐Ÿš€

upbeat pine
#

If you have placed your table inside of a figure, it becomes unable to break across pages by default. However, you can change this behavior. Let's take a look:
this brings the question: why is figure's block is not breakable by default? is block globally not breakable?

upbeat pine
wary knot
#

this wouldnt be desirable for most figure kinds

#

the idea is to display some stuff with a caption right below it

#

so most of the time you don't want it to span a pagebreak

wary knot
upbeat pine
upbeat pine
upbeat pine
upbeat pine
#

oh, no, the proprietary software from micro and soft mentioned. A sad reality...

#

Why would you complicate the example with unnecessary destruction when you can slice 2 instead?

...slice(2)...
let (year, count) = m
wary knot
#

m isn't the one being sliced here though

upbeat pine
#

m is literally the result of the slice.

wary knot
#

no

#

this isn't Rust lol

#

slice doesnt return Option<T>

#

to put it more clearly: moore is a list of lists

#

you removed one list

#

the header

upbeat pine
#

Typst differentiates between grids that are for layout and presentational purposes only and tables, in which the arrangement of the cells itself conveys information.
I wanted to say that double "and" isn't a good thing, and I tripped over this sentence as an example of why it is bad.

upbeat pine
#

yeah, then it's fine.

upbeat pine
wary knot
#

lol

#

i thought you were confusing .map with Option::map or smth

#

since m is clearly not the list itself

#

but an element in it

#

but yeah i mean it happens

upbeat pine
#

option map??? whaat? haha Have I missed something in the basics?

wary knot
#

huh

#

like Some(5).map(|x| x + 1) // Some(6)

upbeat pine
#

Ohh, riiight.

#

I already forgot that you can do this.

wary knot
#

ye but anyway, does the text mention that it's skipping a header? If not, it should, sure

upbeat pine
#

I only have a strong connection with map being applied on list only

upbeat pine
#

Phew, finally finished half-skimming through the guide. It's great but veeeeeeeery long. I already asked a bunch of time "where is the end?" and "it's still going?!"

#

I already remembered that it contains a bunch of nice snippets for different styling of a table. Good stuff. Now I or (more realistically) other user wouldn't have to be smart by making those conditional strokes/fills. Just copy and use it.

robust viper
upbeat pine
#

It literally has guide in the name. afaik the guides are for guiding the newcomers or something, so if I'm not mistaking you are supposed to read it full.

grand haven
#

Guide can mean many things.

vernal herald
#

A User Guide isn't supposed to be read in full prior to usage.

upbeat pine
#

yeah, here you are right.

#

I mean it's small enough to be manageable to read in one sitting, so... yeah.. but not really.

plucky haven
upbeat pine
#

This should probably go into #contributors.

plucky haven
upbeat pine
#

Ah, yes, finally I can add

#let table(..args) = table-old(
  ..args.named(),
  ..args.pos().map(x => if type(x) in (int, float) [#x] else { x }),
)

to the native tables and add pass numbers to it.

dark wasp
#

it should be equivalent, no?

upbeat pine
#

positional would be also table.cell etc.

dark wasp
#

oh, right

upbeat pine
#

The problem with that is now all the table.* things are broken and I either have to import them or use table-old.* instead. Not ideal...

wary knot
upbeat pine
#

This will kill field autocompletion.

dark wasp
upbeat pine
#

Well then it wouldn't be as neat as it is now, now would it? Plus the descriptions will be gone. And I would have to have this massive snippet saved somewhere, because there is no way I can recreate it from scratch and remember all the fields from my memory, unlike the example I showed. Actually, this is a good point, I should add a snippet for it.

dark wasp
#

honestly, I would consider the auto completions where there is an overriding let table a bug

upbeat pine
#

true

#

until we can make a full-fledged copy will all the fields, description etc. while having a different function name, this is the only hack we have. So, waiting for the typed parameters + doc comments.

bold bluff
#

I did a box split:

#

Thanks! @wary knot

wary knot
#

Send this to #1175784402333212733

upbeat pine
#

If kinda tall cell doesn't fit on the page will it be fully placed on the next page or only partially?

#

Because it does the second option and repeating header is inserted in between the cell's content. I think that's a bug.

upbeat pine
#

The "keep caption with table" hack when overriding a figure show rule for table was causing that. But removing figure, it still happens.

#
#let leading = 1.5em
#let leading = leading - 0.75em // "Close enough normalization"
#set page(margin: (left: 3cm, right: 1cm, y: 2cm))
#set block(spacing: leading)
#set par(leading: leading)
#set text(size: 14pt, lang: "ru")

#import table: hline, header
#let head(text, stroke: 0pt) = (strong[#text:], hline(stroke: stroke), [], [],)

#v(22cm)
#table(
  columns: (9cm, 2.5cm, 1.5cm),
  align: (x, y) => if y > 0 and x == 0 { left } else { center + horizon },
  header([ะะฐะธะผะตะฝะพะฒะฐะฝะธะต], [ะงะธัะปะพ ัะปะตะผะตะฝั‚ะพะฒ ะดะฐะฝะฝั‹ั…], [ะ ะฐะฝะณ]),
  ..head[ะ’ะฝัƒั‚ั€ะตะฝะฝะธะต ะปะพะณะธั‡ะตัะบะธะต ั„ะฐะนะปั‹],
  [
    + ะขะฐะฑะปะธั†ะฐ "ะ”ะพะฒะตั€ะตะฝะฝะพัั‚ะธ"
    + ะขะฐะฑะปะธั†ะฐ "ะ”ะพะฒะตั€ะตะฝะฝั‹ะต ะปะธั†ะฐ"
    + ะขะฐะฑะปะธั†ะฐ "ะžั€ะณะฐะฝะธะทะฐั†ะธะธ"
    + ะขะฐะฑะปะธั†ะฐ "ะœะฐั‚ะตั€ะธะฐะปั‹"
  ],
  [
    10--15

    < 10

    < 10

    < 10
  ],
)
#

Not only does the second header add itself in the middle of the cell/row, but it doesn't add the hline for itself, like it always should.

wary knot
#

It overrides the cell stroke below the header

#

You can add an hline below the header if you'd prefer for it to win

wary knot
#

You can use table.cell(breakable: false) if you would like for it to not be broken apart

upbeat pine
wary knot
#

We want hlines to win but we want headers to have priority

#

So we made header strokes have priority over cell strokes , but not over hlines

upbeat pine
wary knot
#

But header hline will always appear

upbeat pine
#

Well, at least now I know how to deal with such problem. Thanks.

wary knot
#

np

upbeat pine
#

Feels like a bug:

#

?r

#let colspan(n, body) = table.cell(colspan: n, body)

#show table.cell.where(y: 0): set table.cell(align: center)
#show table.cell.where(y: 0): it => {
  show table.header: set table.cell(align: center)
  it
}
// #show table.cell.where(colspan: 2): set table.cell(align: center)
// #show table.cell.where(colspan: 2): it => {
//   show table.header: set table.cell(align: center)
//   it
// }
#figure(table(
  align: (left + horizon, left, center, center, center),
  columns: (2.5fr, 1fr, auto, auto, auto),
  table.header(colspan(2)[A], [B], [C], [D]),
  [],
  [],
  [],
  [],
))
upbeat pine
#

how to center content of a *spanned cell? not inline

wary knot
#

I'm extremely aware of that already, don't worry haha

#

At most you could use normal show rules with an #align or smth

upbeat pine
#

there is also breakable stuff, that might get affected.

wary knot
#

That's the best you can do rn

#

Otherwise there isn't much you can do

upbeat pine
wary knot
#

We are engaging in simultaneous human communication

#

The truth is that we live in a society

upbeat pine
#

we live in a society

upbeat pine
#
    align: (x, y) => if y == 0 { center } else {
      (left + horizon, left, center, center, center).at(x)
    },
turbid granite
#

Thought I'd share this comment I ran into about table limitations in LaTeX, I think it might be possible in Typst, but not sure about the status with multi-column support:

https://dercuano.github.io/notes/general-purpose-layout-syntax.html#addtoc_14

This combination of things in LaTeX is annoyingly apparently impossible: multiple-column mode; tables spanning multiple pages/columns; groups of rows within said table which should not be broken apart --- oh, and header rows on the said table which repeat after every column/page break.

(more in the link)

wary knot
#

Text felt a bit dense, or maybe it was 2 am and I was too tired

#

๐Ÿ˜…

#

But if you mean #columns(2) then that should be possible and work

#

Groups of rows - that one we don't have yet

wary knot
#

now this one is interesting

#

i have like zero idea how this could happen

#

๐Ÿ˜‚

upbeat pine
#

Wow, this is the best error ever:

failed to format citation (this is a bug)

#

But that means that someone already predicted this.

#

Oh... there is only one place with that:

โ•ฐโ”€ hg 'this is a bug'
crates/typst/src/model/cite.rs
162:            .unwrap_or_else(|| bail!(span, "failed to format citation (this is a bug)"))
wary knot
#

yeah uh

#

it only happens when the header is repeated

#

or footer

#

i also tested querying a labelled header inside a header or footer and it worked, so introspection isnt broken, at least not fully

#

something weird happened with citations

#

no idea how

#

well ive nailed it down further, only happens on the first page of a repeated header

#

on the following pages it's ok

bold bluff
#

I mean itโ€™s uncommon to put labeled or reference things inside the head or footer so maybe thatโ€™s a issue , but I donโ€™t know

wary knot
#

but theres definitely something wrong somewhere

#

probably some very cursed detail i overlooked

bold bluff
#

When you repeat items across pages is the first one the real McCoy and the rest just visual duplicate or would the reference be repeated?

wary knot
#

admittedly a bit inefficient but im not sure theres another way due to contextual stuf

#

i also made a few more discoveries

#

using 1fr rows for the header or footer also fixes it

#

and auto row makes the footer error on the first page but not on the rest (just like the header)

#

but fixed-size row for footer makes it not error at all, while header still errors

#

so. very interesting

bold bluff
#

So if I was to put a reference in the header <mylabel> that would probably cause problems if it had more than one copy

wary knot
#

i also broke the compiler somehow lol

#

it's loading forever

#

i typed rows: (1%, 2em)

#

lol

#

hmm interesting

bold bluff
wary knot
#

ok for some reason rows: (1%, 2em) makes the compiler go insane

#

lol

#

actually it has a memory leak and dies

#

no idea what's that one lol

bold bluff
#

Iโ€™m assuming non relative sizes donโ€™t cause issue

wary knot
#

but i assume you mean non-ratio

turbid granite
#

I am loving these updates. This is so cursed

wary knot
#

๐Ÿ˜‚

#

i still dont have any idea what this could be

#

and especially why it only happens at the first page of the table

upbeat pine
plucky haven
#

Did we discuss about grid for mat?

#

#contributors message

#

I saw the mat added augment. I didn't like it as it is too specific for concrete cases. It seems just a grid.vline or grid.hline.

upbeat pine
#

I think it is just an oversimplified version of those two. I also didn't like it, and AFAIK you can only change stroke for all of them at once.

velvet garnet
#

Found a nice workaround for this until the show set rule is fixed for table headers:

I usually have a wrapper function to style my tables, so I pass in a value for header-rows and do...

let selectors = for i in range(1, header-rows) {(table.cell.where(y: i),)}
show table.cell.where(y: 0).or(..selectors): strong

The for loop builds an array of selectors to pass to the or() method. Would need to wrap the styling function in an if statement to cover the case where you might have no header.

You could instead pass the header in as a named argument and actually work out the number of rows automagically, but find this way works OK for me.

upbeat pine
#

?r

#let header-rows = 5
#show: doc => {
  show: it => {
    let selectors = range(header-rows).map(i => table.cell.where(y: i))
    if selectors.len() == 0 { return it }
    show selector.or(..selectors): strong
    it
  }
  doc
}
#table(..([h],) * header-rows, [a])
bold bluff
#

I found a odd thing

#

?render

#table(
  columns: (3.47em, 7.88em, 5.42em, 13.22em, 5.42em),
  rows: (8.05em, 4.06em, 5em, 11.23em),
  stroke: 1pt,
    table.cell(
      box(
        width: 3.47em,
        height: -2pt + 5em,
        inset: 0pt,
        clip: true,
        align(
          center + bottom,
          box(inset: 0.2em, [H]),
        ),
      ),
      x: 0,
      y: 2,
      colspan: 1,
      rowspan: 2,
      fill: black,
      inset: 2pt,
      stroke: 0.5pt + luma(0%),
    ),
)
bold bluff
#

The "H" although it is told to be aligned to the bottom kind of floats in the middle.

#

it might me my fault. cause the height seems weird.

frail mortar
#

It's aligned with respect to the box which is only as high as the 3rd row

bold bluff
#

Yes. yes it is. ๐Ÿ™‚ ๐Ÿ™‚ :0 ๐Ÿ™‚

#

?render

#table(
  columns: (3.47em, 7.88em, 5.42em, 13.22em, 5.42em),
  rows: (8.05em, 4.06em, 5em, 11.23em),
  stroke: 1pt,
    table.cell(
      align(
          center + bottom,
        box(
          width: 3.47em,
          height: -2pt + 5em,
          inset: 0pt,
          clip: true,
          box(inset: 0.2em, [H]),
        ),
      ),
      x: 0,
      y: 2,
      colspan: 1,
      rowspan: 2,
      fill: none,
      inset: 2pt,
      stroke: 0.5pt + luma(0%),
    ),
)
bold bluff
#

That should fix it right?

#

Nm it only still affects the box stuff.. boy what a day.

grand haven
robust viper
#

I think without introspection trickery its difficult

#

but with introspection it's possible I think

grand haven
#

Probably possible, but more effort than I'm willing to put it in right now for just a referee report :p

robust viper
grand haven
#

I just noticed something else. Is this a bug?

#

?render ```
#set page(width: auto,height: 2cm)
#table(
columns: 2,
stroke: none,
table.cell(rowspan: 3)[A],[B],[C],[D],table.hline()
)

grand haven
#

or, maybe not a bug, but not always desirable

robust viper
#

cc @wary knot

grand haven
wary knot
#

if i recall correctly it's how it works, the last hline is repeated on every page

grand haven
#

the last hline in every table you mean?

wary knot
#

yes

#

using cell stroke instead of hline could even it out in terms of priority

#

cuz hlines are always top priority

#

but i dont remember the full semantics for conflict between the repeating stroke and previous strokes, i think a previous stroke wins if it's higher priority, but not if it's <=

upbeat pine
# grand haven ?render ``` #set page(width: auto,height: 2cm) #table( columns: 2, stroke: n...

FYI, I had a hard time reading this until I formatted the code (too much cramped to a single line). It's better to do that, if you have a chance.

#set page(width: auto, height: 2cm)
#table(
  columns: 2,
  stroke: none,
  table.cell(rowspan: 3)[A],
  [B],
  [C],
  [D],
  table.hline(),
)
grand haven
#

@wary knot okay there's definitely something weird

#

?r ```
#set page(width: auto,height: 2cm)
#table(
columns: 2,
stroke: none,
table.cell(rowspan: 3)[A],[B],[C],[D],table.hline()
)

grand haven
#

?r ```
#set page(width: auto,height: 2cm)
#table(
columns: 2,
stroke: none,
table.cell(rowspan: 3)[A],[B],[C],[D],table.hline(),[E],[F]
)

grand haven
#

So this matches what you said about the last hline

wary knot
#

looks correct to me

#

the idea is to allow proper borders around tables

#

it makes more sense when you include the other lines that repeat too (top, left, right)

#

w/ that said, we could consider having some way to opt out from this behavior

#

but it was quite hard to find a good enough API for all use cases :p

grand haven
#

yeah but I have situations where this doesn't act this way

#

struggling to find a mwe

wary knot
#

it all depends on table stroke vs cell stroke vs v/hline

#

typst computes priority at each cell border

grand haven
#

I have no strokes, only an hline after the header and between each page

#

there should be no line at the bottom of the first page in the picture

wary knot
#

i cant tell without the code

grand haven
#

I know, that's why I wanted to find an mwe

wary knot
#

but in principle if theres any stroke at the bottom it will repeat

#

period

#

w/ that said, it can be overridden if it's lower priority in previous pages

grand haven
wary knot
#

im not talking about per-cell stroke, but any kind of stroke

#

including stroke: something in the table

grand haven
#

@wary knot okay I finally figured it out. I'm just too sleepy. It was truly just the repeating last hline in the table

#

But I'd like to disable that behavior

#

There's no special significance to it in my case

#

Shouldn't the table.footer be used for what you're talking about?

wary knot
#

it's similar to blocks with stroke, except that you can control the lines more precisely than just a single line crossing the whole bottom of the table

wary knot
grand haven
#

my point was that there's some special significance to the last line in a table, and to me that sounds like something that should be related to the footer instead

wary knot
#

footers are more about repeating cells , this is a separate concept, it's more of a style choice than anything

#

an issue would be good to collect other people's opinions so we can know which api works best

lone nova
#

I understand that tables in Typst don't work well with rotated text, right?

Even if you set reflow: true, it just causes the text to overlap the cell borders and go beyond them.

grand haven
#

That sounds like a bug

#

probably works on main @lone nova

lone nova
#

?r

#set page(width: 50mm, height: 70mm, margin: 0.5cm)
#set text(size: 10pt);

#table(
  columns: 2,
  gutter: 5pt,
  table.header(
    table.cell(colspan: 2, text(size: 1.5em)[*Some name*]),
  ),

  rotate(-90deg, reflow: true, text(size: 1.1em)[Religion]),
  rotate(-90deg, reflow: true)[#lorem(15)],
)
grand haven
#

Okay so then I don't know, but it's unlikely to be specific to tables. What happens in a rect?

upbeat pine
#

You have to use ?r separately from your message.

lone nova
#

?r

#set page(width: 70mm, height: 70mm, margin: 0.5cm)
#set text(size: 10pt);

#stack(
  dir: ltr,
  rect(rotate(-90deg, reflow: true)[#lorem(25)]),
  rect(height: 50mm, rotate(-90deg, reflow: true)[#lorem(25)])
)
grand haven
#

?render ```
#set page("a10")
#rect(rotate(33deg, reflow: true,[Religion]))

grand haven
#

though I can't reproduce the last example on main

#

the table I can

#

@wary knot table shenanigans?

wary knot
#

it first measures width and then measures with fixed available width to figure out the height

#

assuming auto columns and rows

grand haven
#

I guess they should create an issue

timber bronze
#

What does setting gutters to auto mean? This is not explained in the doc, and auto for Sizing usually means "fit the element's content", but for gutters there is no content.

#

As noted in #3959 (comment), auto on {column,row}-gutter does not mean "inherit the value from gutter".

robust viper
timber bronze
#

Does this mean auto always resolves to 0pt for grid gutters? Then, wouldn't it be better to use auto on {column,row}-gutter to inherit the value from gutter, and not support auto on gutter?

#

And what's the point of being able to pass an integer n as a shorthand for n * (auto, ), which, if I understand well, is the same as simply auto, i.e., 0pt?

robust viper
#

There isn't really a point. It's really just that both use the TrackSizing type in Rust.

wary knot
#

I considered outright removing it during the table rework, but it felt equally pointless to do so

#

ยฏ_(ใƒ„)_/ยฏ

#

It may interact a bit weirdly with rowspans, but i remember i at least considered that case

timber bronze
#

Ok. I'll try to see if I can change that

wary knot
timber bronze
#

It's not great user experience in my opinion, and would let us close #3959 (which is not a bug, but still)

GitHub

Description The documentation for grid states that the default value for grid.column-gutter is (), yet when setting it explicitly, it changes the behavior of grid. #grid( columns: 2, gutter: 1em, b...

#

?r

#table(
  columns: 2 * (1cm, ),
  gutter: auto,

  [hey], [ho],
  table.cell(colspan: 2)[spaaaaaaaaaanned],
)
timber bronze
#

I guess this is a case where row-gutter: auto makes sense, but this requires more work than just not allowing auto for gutter

robust viper
timber bronze
#

I think it makes it easier to understand for the user, for two reasons:

  • This is how a user would have to implement a shorthand-like argument in a custom function (without using argument sinks).
  • It would improve the documentation (see the aforementioned issue).
heady tide
wary knot
#

conceptually, i think some way to configure a particular gutter around some row would make more sense than attaching it to table lines, cuz in practice theres no relation between gutters and lines

#

in particular ive had the idea in the back of my mind for a while now of having some sort of table.row or table.rows primitive to configure a row / group of rows on the spot

#

but at least for me thats gonna be lower priority , there are other table things i want to do first

#

like i'd love to get the multiple headers idea going at some point

#

worth clarifying that none of this would come for 0.12 though, just some thoughts for the future

upbeat pine
#

||I can't forward without somehow making an old thread recent... so stupid||

#

Instead of this:

#show table.cell.where(y: 0): strong
#table(
  columns: 2,
  align: (_, y) => if y == 0 { center } else { left },
  table.header[ะ˜ะผั][ะ ะพะปัŒ],
  ...
)

You would simply do this:

#show table.header: set align(center)
#show table.header: set text(weight: "bold")
// #show table.header: strong
#table(
  columns: 2,
  align: left,
  table.header[ะ˜ะผั][ะ ะพะปัŒ],
  ...
)
wary knot
#

until that's fixed it's a no go

upbeat pine
#

okey-dokey

#

My main concern is will Typst allow this, since, IIUC, the intended way is to use alignment callback in the table constructor.

wary knot
#

it wouldnt be too different from #show table.cell: set align(...), which generally doesnt look very good

upbeat pine
#

btw, does this snippet work?

#

I haven't tested that.

wary knot
#

table.header is never laid out as an element, so show-set doesnt have an effect

#

as well as just show

upbeat pine
#

This is what I wanted to ask. If you can do that for the table cell show rule, then it makes sense that you can do the same with header.

wary knot
#

it's desirable, yes

#

the reason it doesn't work is that the header's cells are laid out, not the header itself

upbeat pine
#

does this work

wary knot
#

probably not as you'd expect

upbeat pine
#

hm

wary knot
#

ideally you'd write #show table.cell: set table.cell(align: ...)

#

but that's not possible atm (it's a separate issue)

#

similarly for your header proposal you'd be able to write #show table.header: set table.cell(align: ...)

upbeat pine
#

oh... So both will work in the end? this and previous show-set rule?

#

Isn't this... too much of ways to do the same thing?

wary knot
#

#show table.cell: set table.cell(align: ...) is probably always gonna be preferred

#

if it's ever supported

upbeat pine
#

it will, I mean at least some way should be supported

wary knot
#

lol

#

thats not what i meant, i meant unless we change how tables work more fundamentally

wary knot
upbeat pine
wary knot
#

roughly set align applies to the cell as a whole, including stuff coming from other table.cell properties, while table.cell(align: ...) applies only to its body

#

thats not 100% accurate but thats more or less the idea

upbeat pine
#

stuff coming from other cell properties?

wary knot
#

like align itself and also inset

#

i wont remember now the exact list but it's in the show rule for cells

upbeat pine
#

I thought of: show cell: set align() will modify the alignment of all blocks inside... This doesn't make sense. I think either way everything inside will inherit the new alignment. So show cell: set cell(align) should would the same in this regard.

wary knot
#

yeah it's recursive, so the body is always affected

upbeat pine
wary knot
#

and it was actually writing align(x)[abc] vs table.cell(align: x)[abc]

#

which is different from the show-set rule

#

table.cell is basically shown as align(cell.align, pad(..cell.inset, body)) , and the pad would have the effect of "breaking" the alignment inside the body

#

so align(x)[abc] wouldnt work

#

so there probably wont be much of a difference now, though there might be some edge case im not aware of

random mica
#

What if Typst could infer the width of a table from the array provided to table.header?

#

That would be so convenient and make some sense imo; I think tables are really missing an easier way to specify dimensionality without a hard coded number

grand haven
random mica
#

to clarify; I mean width as in # of columns

#

๐Ÿ˜…

grand haven
#

Not all tables have a header though, and I don't think it necessarily needs to have the same number of cells as the table

random mica
#

For sure, but as a default if the columns parameter of a table is auto, I think it's pretty sane to set it to the length of the header

grand haven
#

It sounds a bit magical

#

Not saying the current situation is ideal

random mica
#

It sounds like a pretty good way to get that information from the user 'seamlessly' and it encourages always using table.header (which as its documentation suggests is a good idea regardless)

#

(of course, only if the table logically has a table)

supple eagle
#

Reminds me of how for [a, b, c] in iter.array_chunks() works without explicitly setting array_chunks::<3>()

upbeat pine
random mica
signal breach
#

I've just added a feature request for the tabulate python package https://github.com/astanin/python-tabulate/issues/358 to add Typst support.

It is a very usefult package and is frequently used with pandas dataframes to get nicely formatted tables. It supports loads of output formats (markdown flavors, html, LaTeX...) and it would be great to have there some Typst support.

GitHub

By the Typst project definition, "Typst is a new markup-based typesetting system for the sciences. It is designed to be an alternative both to advanced tools like LaTeX and simpler tools like ...

signal breach
upbeat pine
#

Is it currently difficult to make a reactive (to page) table figure caption? For long tables.

upbeat pine
grand haven
wary knot
grand haven
#

Maybe the feature request itself, but it was only implemented very recently

#

Do you think something similar could work for typst?

wary knot
#

perhaps we could just use labels and show-set

#

at least this works right now...

#

?r ```js
#show <red-cell>: set text(red)
#table(
[a],
[#table.cell[ast] <red-cell>]
)

wary knot
#

but unfortunately it is limited, since you cant show-set on table cell properties atm, as well as on headers

#

also you cant label a whole row. i suppose this could be one usecase for table.row

#

and the fact that you have to use markup mode to attach a label is a bit annoying and even misleading, dare i say, so i guess that's another argument to support labels in code mode

robust viper
#

The issue that table input is a bit annoying (for manual tables, but also for data-generated table) because cells jump around if one is missing has come up a couple of times now. But, more generally, in my opinion, creating and styling complex tables is a bit of a hassle currently.

Aside from table.row (and possibly table.column), which were proposed before, I'd also like to throw a table.region function into the ring. This would be a rectangular subregion of a table, with the following characteristics:

  • On the region, you can set parameters like stroke etc. just like on the table.
  • Within the region, the x and y parameters of cells are relative to the region.
  • Within a region, a table.row and table.column are local to that region.
  • The region could be used in show/set rules.

The main benefit is that this makes it easy to decouple data cells from "meta" cells instead of having to stitch everything together into a single stream of cells and having complicated styling rules based on x/y.

The idea is quite fresh and untested and I haven't gotten around to experimenting with how well this would work out in practice, but I think it could be quite useful. Typically, it's not random if a table styling differs across parts of a table. It's because for the human reader, the table decomposes into different pieces with different meaning. Making this explicit could make styling quite a bit easier.

wary knot
#

Though I'm not sure about the positioning, having x / y be relative to the region sounds circular at first as the region in theory would inherit the position of its children, but i suppose thats a less useful behavior compared to manually setting something like top left corner + width and height

#

So there's some room for flexibility, but I think I'd create separate fields such as dx/dy to ensure x/y in table.cell has only one meaning

robust viper
mossy rampart
#

Also somewhat related to this, there is currently no way of expressing header columns, or just singular header cells (which could be regions I guess).

vagrant pagoda
#

How does region link with rows and how is it determined how big a region is? I would aim to avoid having to specify explicit column/row counts as much as possible for direct table editing. Let's say I input a region with cells (a, b, c, d)(e, f, g, h) - that's 2 x 4 and I don't need to tell the function the size of the region is 2 x 4, hopefully.
With row oriented editing, table(row[A, B], row[A, B, C]) would be a perfectly fine table and it implicitly has 2 rows and 3 columns, that's how I'm thinking about it. (Taking some syntax shortcuts here, if it's table.row[A][B] or something else is a separate issue)

robust viper
vagrant pagoda
#

Another thing. I'm also making experiments with "setrow/setcol" directives. Also a little bit inspired from tabularray (thought it is a deprecated syntax there).

It looks something like this - inserting directives to color specfic rows:

#show table: setrowrules
#rowtable(
  stroke: (x: 1pt, y: 0pt),
  [Alpha & Beta & Gamma #setrow(cyan)],
  [Epsilon & Zeta & Eta #setrow(aqua)],
  [Iota & Kappa & Lambda #setrow(violet.lighten(50%))],
)

On it's own it's a bit meh, but it has a benefit that we again put the row style physically in the row it should affect, sometimes that's good.
It's also meh because in a table.row world this could just be table.row([...], fill: cyan) but there is more.. (Also but: We can't have both table.row and table.column at the same time; but we can easily do insersecting setrow and setcol at the same time)