#Tables
1 messages Β· Page 6 of 1
Shouldn't the cells be numbered according to their position in their first line? Like so:
1 2 3
4 5
6 7 8
9 10
11 12 13
14 15 16
17 18
19 20 21
That would make more sense imo, though I don't know how hard it would be to make it work
The numbering isn't something the user is going to be exposed to, so it doesn't necessarily have to make complete sense.
As far as I understand
Hm yeah
here
Like footnotes
If it's hard to fix it's not the end of the world if that doesn't happen before 0.12 though?
Well, it's technically possible , but it would be quite a bit of extra work
I'm afraid it wouldn't get into 0.11 in time , if we decided upon that
With that said, i think the behavior I achieved should be at least consistent
Though i still haven't decided on what happens at the last row
ATM the rowspan content is placed before the last row (but after the previous ones)
But it could also be placed after the last row. Idrk
I think it doesn't matter in the end so I'll probably not touch it
Btw, if anyone has rowspan tables they want tested, just ping me π
#let kn = 0.002
#let vdd = 5
#let vtn = 1.5
#let voh(r) = calc.round((vdd - (1/(2 * kn * r))), digits: 3)
#let vol(r) = calc.round((calc.sqrt((2 * vdd) / (3 * kn * r))), digits: 3)
#let vih(r) = calc.round((vtn - (1 / (kn * r)) + (1.63 * calc.sqrt(vdd / (kn * r)))), digits: 3)
#let vil(r) = calc.round((vtn + (1 / (kn * r))), digits: 3)
#let nmh(r) = calc.round((voh(r) - vih(r)), digits: 3)
#let nml(r) = calc.round((vil(r) - vol(r)), digits: 3)
1. #block[
#figure(
kind: table,
tablex(
align:horizon,
columns: 8,
rows: 5,
header-rows: 1,
repeat-headers: true,
auto-vlines: false,
auto-hlines: false,
map-cells: cell => {
if cell.y == 0 {
cell.content = strong(cell.content)
} else if cell.x > 1 and cell.y > 0 {
cell.align = right
}
if calc.rem(cell.y, 2) == 1 {
cell.fill = gray.lighten(50%)
}
cell
},
colspanx(2)[Inverter], (), [$V_("OL")$ (V)], [$"NM"_upright(L)$], [$V_("IL")$ (V)], [$V_("IH")$ (V)], [$"NM"_upright(H)$], [$V_("OH")$ (V)], hlinex(),
rowspanx(2)[NMOS, $1 upright(k) ohm$ load], [Measured], [1.1], [#calc.round((2.2 - 1.1), digits: 3)], [2.2], [4.1], [#calc.round((4.61 - 4.1), digits: 3)], [4.61],
(), [Calculated], [#vol(1000)], [#nml(1000)], [#vil(1000)], [#vih(1000)], [#nmh(1000)], [#voh(1000)],
rowspanx(2)[NMOS, $20 upright(k) ohm$ load], [Measured], [0.19], [#calc.round((1.5 - 0.19), digits: 3)], [1.5], [2.2], [#calc.round((4.78 - 2.2), digits: 3)], [4.78],
(), [Calculated], [#vol(20000)], [#nml(20000)], [#vil(20000)], [#vih(20000)], [#nmh(20000)], [#voh(20000)],
),
caption: [
Inverter parameters for the resistor-load inverters
]
)
I don't know if that one is any more complex than the one's you've likely been testing
Ayyy thank you π
Don't worry it will be very useful
I really just need real-world examples, i think my random tables with a a a a a a a a in them don't cut it π
Ah lol
This is what it looks like with tablex 0.0.6 (and some extra show rule shenanigans on the caption)
Ty!
I'll test it soon
yay im back to my linux PC
https://github.com/typst/typst/issues/3513 @wary knot has been naughty
#contributors message
For some reason I managed to overlook that
but i mean i have no idea what could have caused that difference in behavior
however they are indeed doing some weird stuf
If the wrong alignment is used, why this is still an issue again?
Oh... I didn't understand the second comment.
I mean, isn't this a "this is not how things work" case? For me, since no one should've used that, it's totally fine that this change has happened.
@wary knot I did a bisect. The change happened in https://github.com/typst/typst/pull/3037.
The show rule is pretty cursed, but I still wonder what caused it.
hmm yeah idk
could perhaps be related to inset
or maybe it's just that we have some more element nesting with table.cell everywhere, idk
yeah that's it I think
the flow won't be able to see the alignment anymore
I've closed the issue.
lol. turns out this was just a bug
(cc @bold bluff )
the cell has a bottom stroke, but it wasnt being applied
cuz this is pretty much an edge case which is when you specify a rowspan cell occupying multiple auto rows all by itself (occupies 100% of columns)
visually you only see one row but hlines dont (or didn't) work with visuals
much better now
Sounds tricky. But it looks pretty good. Perhaps a good debug style would be helpful. Something where all the attributes of the table would be visible just for verification.
well i guess that'd be .fields()
but yea
basically what i changed is that the top / bottom borders now fold with the cell strokes, they dont totally override them anymore
but the borders always have priority
so it's a step forward
ideally we'd have stroke: (outside: ...) but this will be enough for now
also lol
the test broke but i think it was for the best
before rebasing
after rebasing
π
What?
π€
You have to assume, that I just don't see or get things discussed here. π
Repeating headers in multiple page tables! π
i made a quick experimental version in like 30 min (building upon the rowspans PR, which has a good chunk of the hard work done)
that experimental version is probably enough for most simple cases to be honest lol
but non-simple cases are the big sad
hmm
i think the non-simple cases are mostly restricted to auto rows honestly
which is basically like, when u have a multi-page auto row, u have to make sure the continuation of the auto row doesnt go over the header
and ive mostly tackled this right now in an experimental way
well, that works for me!
aint no way
there has to be something else to do (yes there is I have to make a user-facing API but let's ignore that for now)
i mean okay there are N ways i could improve this code
but lol
it's really that simple
oh right
rowspans exist
sec!!!
Ah didn't notice that. Thanks for the explanation.
i was intentionally cryptic to raise suspense
im sorry
π
ok epic (no idea why the thing at the bottom does that but anyway)
ok so
should the gutter gutter or what should the gutter gutter gutter
(me on the verge of deleting the gutter parameter)
What happens if the table header row size equals to the page size or, even worse, larger than the page size?
There should probably be at least some kind of warningπ€·ββοΈ
i thought about it
but i was afraid of just going back to LaTeX where you have warnings like that everywhere
lol
i feel like the warning will be the document itself
lol
and regarding what happens: headers are unbreakable sooo you'll just get a screwed up table
π€£
probably cuz of my orphan header check
skips a region if it would just have a header
but i should probably push some empty content / frame instead
ok so
maybe that's not a good idea
or maybe it is and it's your fault that you made a screwed up thing π
here's your table!
π
lol
i think i found the perfect page height
it seems to cause an infinite loop
of pagebreaks
how are you even awake? it's like 5am where you are, isn't it?
maybe you've tricked us all and you aren't in Brazil in the first place!
it's good to work out
indeed
but yeah i mean
table headers are a thing now sort of
π
a very simple version but yea
all thanks to huge amounts of hard work in the rowspans PR :p
first version came out in 30 min
without handling any of the extra complexity
second version (ensuring auto rows and rowspans recognize headers exist) took two hours after that
:p
anyway yeah epic
now i dont know why the layout engine is in an infinite loop
but that's something for another day
oh god i actually just noticed it
it's so horrible
and sad
it's in the rowspans PR
π
see this is why you make headers at 5 am, you prevent a natural disaster
clearly something is off here π
Dude, go to sleep π
i will but that was too horrible to overlook
π
(im advancing the regions variable and checking self.regions... whoops)
done (that wasnt the only problem but anyway)
the useless example of what not to do lives
π
ok ill sleep now
cya
@wary knot something fishy is happening with this code:
#set page(width: 10cm, height: 6cm, margin: 1cm)
#set text(size: 11pt)
#table(
columns: (auto, 4cm, 1fr),
align: center,
[Hello],
table.cell(rowspan: 2)[There \ You \ Are!],
[World],
[What],
[Up],
[Now],
table.cell(colspan: 2)[Here we go.],
table.cell(colspan: 3, lorem(10)),
[Continuing],
table.cell(rowspan: 3, lorem(8)),
[Here],
[A],
[B],
[C],
[D],
)
I havent checked, but is text going off cell bounds?
okay i just checked
seems normal to me
unless im missing something?
agh
screenshot tool ate some letters
not last auto row
lol
also I plan on undrafting today btw
I already updated the PR description and stuff
so you can read that in the meantime if you want to :p
why is it breaking into three pages when it would fit into two? and why is there space above the lorem(8)?
I had already started, thought I might as well fill the pipeline.
I'm about 2/3 through the review.
oh ok
im gonna push a small fix, not sure if you caught that in your review
of using the correct pod.full in measure_auto_row
for the unbreakable case?
all cases
it's always using the full of the auto row's region
but if the cell starts in a previous region, that region's has to be used instead
and we store that data in the Rowspan struct so it's ok
ah
I wouldn't have. I'm afraid I won't be able to find tricky bugs, but I am giving my best to at least understand what's going on.
I guess GitHub will be able to handle it
i caught this mostly cuz im reviewing by myself too hehe
the more the merrier as they say
not sure whether you saw this message: #1182377294682128445 message
yeah
im not 100% sure of why there was a pagebreak but in principle it's possible it just triggered the can_skip branch
also the margin is rather significant there
so that might have had an influence in the space available
i cant say i can really explain that behavior fully though but seems like a rather complex interaction of things
if you replace table.cell(rowspan: 3, lorem(8)) with table.cell(rowspan: 2, lorem(8)) you can see that it should fit pretty easily
and then there is the fact that the Lorem ipsum is bottom aligned for some reason
it seems to me like it is just placed in a wrong way by the layouter
(even with rowspan 2 this weird alignment happens)
hmmm
somewhat minimized:
#set page(width: 10cm, height: 2.5cm, margin: 0.5cm)
#set text(size: 11pt)
#table(
columns: (1fr, 1fr, 1fr),
[A],
[B],
[C],
[D],
table.cell(rowspan: 2, lorem(4)),
[E],
[F],
[G],
)
does not have the extra break, but still the weird shift
i cant really explain that right now
seems like an artifact from flow in general
of note: I use (true, true) region expand when laying out rowspans
that is correct, right?
(when measuring the auto row it uses self.regions.expand which is usually (true, false) )
idk if that could be part of it but worth saying
OH
I KNOW WHAT IT IS
π
ok give me one moment
wow
it's really subtle π
well
at least what the "extra break" problem is
one down @robust viper π
I'm now down to the last remaining function to review: simulate_and_measure_rowspans_in_auto_row.
tl;dr this was right but it was deeper
it was checking the first region of the rowspan to see if it's empty there but that doesnt matter
now it skips frames from previous regions
btw @robust viper im not sure if you address that in your review but what are your thoughts on having cell.breakable being resolved on the fly?
for one, it's a bit inconsistent with the remaining cell properties, cuz they are all resolved before show rules, while that one property would remain as auto
but also i thought that the way Typst resolves breakable would be a bit of a, perhaps, low-level detail
yeah i can
right now i just store it as auto and delay the work
but maybe it's not that bad to expose it to the user
it being whatever we resolve
so i think ill just do it
well actually theres a small problem
we'd need access to the rows and check gutter and stuff while that stuff is being built
sounds like a pain
checking it later is simpler cuz we just use grid.rows
i think ill keep it as is for now, i dont expect people to be writing show rules with cell.breakable anyway :p
actually, i messed with it and it's possible, but intersperse would make it sooo much easier lol
to simulate rows + gutter
but okay
(seems like it's a nightly feature)
this here illustrates that it's probably rather a problem with positioning than sizing
#set page(width: 10cm, height: 7cm, margin: 1cm)
#set text(size: 11pt)
#table(
columns: (1fr, 1fr, 1fr),
gutter: 5pt,
align: center,
..upper("abcdef").clusters(),
table.cell(rowspan: 4, lorem(13)),
..upper("abc").clusters(),
table.cell(rowspan: 2, breakable: false, lorem(10)),
..upper("hijklm").clusters()
)
I found another bug I think:
#set page(width: 10cm, height: 10cm, margin: 1cm)
#set text(size: 11pt)
#table(
columns: (1fr, 1fr, 1fr),
align: center,
rows: (4cm, auto),
[A], [B], [C],
table.cell(rowspan: 4, breakable: false, lorem(10)),
[D],
table.cell(rowspan: 2, breakable: false, lorem(20)),
[E],
)
somehow the second, large unbreakable rowspan doesn't lead to row group to shift to the new page.
tablex handles this case correctly.
will take a look too
this also illustrates it from your previous example
basically just replaced lorem(8) with a rectangle
(the tables are cooking so hard right now π²)
kill 'em! π« burn them to the ground! π₯
ok i have a theory
im not 100% sure but
basically in layout_rowspan there is a dy which is supposed to be the dy of the rowspan in the first region
for all further regions it's zero
but
we zip with the fragment returned by the laid out rowspan
so it's possible that there could be a mismatch between first region and first frame
or something
the problem is somewhere in there, i'll take a further look soon (gonna eat lunch now so brb for a bit)
I've left my review. It's mostly just stylistic things as finding logic bugs is too hard here. But I have at least roughly understood how everything fits together.
This means that I probably wouldn't understand a thing...great.
haha yeah
it's definitely not a trivial system
but I guess that's what happens when we solve non-trivial problems :p
but at least the PR description should have a good overview over what's going on
or at least what I considered while writing the code
So, is the today's review and patching final?
wdym?
Will the PR be merged today?
depends on multiple factors
Because it looks like it.
lol
ok
yeah, It would be cool to have a new release on the first day of spring. Or rather warm.
march 19?
why 19?
google told me thats the first day of spring in the northern hemisphere
no thats not when spring starts
In Russia it is. Because spring is March, April, May. Other 3 seasons have other 9 months. Otherwise it would be too complicated.
yea but it's not in the first day of march
sure you can simplify it to "whole march" if you want but thats not how geography works :p
but either way
no the release isnt coming on march 1st
it's impossible :p
oh, well...
well thats a different definition of spring then (not the geographical one but some government-defined thing)
cuz spring starts after the equinox
which is the day when the day and night are equally long
but anyway
off-topic lol
TIL
im still trying to figure out whats going on here
layout_rowspan seems to be doing OK so far during debugging
ive printed the frame items for the fragment and it seems that the cell itself is placing the rectangle there
so that's odd
of note, this happens when inset is disabled for that particular cell
seems like the top inset is growing too much or something? weird
but yeah something is definitely fishy
(almost) same cell contents
adding a single letter at the top makes the rowspan behave properly
very offtopic, bur there is astronomical spring (starting around march 21) and meteorological spring (starting march 1) (on the northern hemisphere, respectively).
thx π
i was thinking of the first one yeah
as an update: im deep in the weeds running the debugger , and up to the inset everything is fine it seems
but then during a second flow layout run something changes
i assume that's the alignment
but idk
something changes the rectangle's position from 5pt (correct, inset) to 22pt (wat) (aka 2em)
the angry little men in my RAM must be very angry
this is if I add #place(top + left)[hi] before it
but if I don't then it gets moved to (5pt, 15pt) instead of the otherwise correct (5pt, 5pt)
very odd
hmmm
it seems that theres some invisible something with height 10pt above the rectangle
after spending a good amount of time stepping through flow code
lol
Is there β¦ misplaced gutter effects ?
no
i was thinking about it
and i think it's inset from the previous page
i.e. this invisible thing was supposed to be up here
seems to be the only plausible explanation so far
cuz it seems to always be 2x inset
also btw
regarding headers
i do indeed think that they wont make it to 0.11 in time
my implementation is cool but it's naive
if my mental model is correct, it doesnt properly preserve ratios (100%, 50% etc.)
so that could be a problem
and fixing it would be everything but trivial
:p
What makes the invisible inset move across page boundaries?
ive fixed this one now
basically needed to measure with infinite height
also added some .max(zero) around cuz apparently one of the rows was being measured with negative height
lol
yeah thats a great question
maybe it somehow couldnt find space in the first page
but while i was debugging that didnt seem to be the case (there was 17pt available in the first page and it requested 10pt)
i wonder if it's some sort of bug in the text flow code, idk
here you go btw π
code:
||
#set page("a5")
#let kn = 0.002
#let vdd = 5
#let vtn = 1.5
#let voh(r) = calc.round((vdd - (1/(2 * kn * r))), digits: 3)
#let vol(r) = calc.round((calc.sqrt((2 * vdd) / (3 * kn * r))), digits: 3)
#let vih(r) = calc.round((vtn - (1 / (kn * r)) + (1.63 * calc.sqrt(vdd / (kn * r)))), digits: 3)
#let vil(r) = calc.round((vtn + (1 / (kn * r))), digits: 3)
#let nmh(r) = calc.round((voh(r) - vih(r)), digits: 3)
#let nml(r) = calc.round((vil(r) - vol(r)), digits: 3)
1. #block[
#show table.cell: cell => {
if cell.y == 0 {
strong(cell)
} else {
cell
}
}
#figure(
kind: table,
table(
align: (x, y) => if x > 1 and y > 0 { right } else { horizon },
columns: 8,
rows: 5,
stroke: none,
fill: (_, y) => if calc.odd(y) { gray.lighten(50%) },
table.cell(colspan: 2)[Inverter], [$V_("OL")$ (V)], [$"NM"_upright(L)$], [$V_("IL")$ (V)], [$V_("IH")$ (V)], [$"NM"_upright(H)$], [$V_("OH")$ (V)], table.hline(),
table.cell(rowspan: 2)[NMOS, $1 upright(k) ohm$ load], [Measured], [1.1], [#calc.round((2.2 - 1.1), digits: 3)], [2.2], [4.1], [#calc.round((4.61 - 4.1), digits: 3)], [4.61],
[Calculated], [#vol(1000)], [#nml(1000)], [#vil(1000)], [#vih(1000)], [#nmh(1000)], [#voh(1000)],
table.cell(rowspan: 2)[NMOS, $20 upright(k) ohm$ load], [Measured], [0.19], [#calc.round((1.5 - 0.19), digits: 3)], [1.5], [2.2], [#calc.round((4.78 - 2.2), digits: 3)], [4.78],
[Calculated], [#vol(20000)], [#nml(20000)], [#vil(20000)], [#vih(20000)], [#nmh(20000)], [#voh(20000)],
),
caption: [
Inverter parameters for the resistor-load inverters
]
)
]
||
Why did you add kind: table? Reflex?
But that one used tablex, so it was understandable.
yea
i just copy pasted their code and replaced tablex stuff with not tablex stuff
so missed that one
:p
You have a great opportunity to change that. :) ||immediately π«||
@robust viper Q: should we measure all unbreakable auto rows with infinite height, even outside of the unbreakable auto row simulation (where using infinite height is needed to fix the overflow bug you had shown)?
as in, do you think theres a situation where we wouldnt want that?
β οΈ Infinite height is needed to fix the overflow bug β οΈ
lol
well
it's cuz it's a bit funny
the idea is to measure things in the auto row to see how large they will be right
well
if you set the height at e.g. 50pt for some text
and it's unbreakable, meaning it cant spill over to the next page
then the text's height will be exactly 50pt
i think i'll just do it
What would the consequences be?
it'd be the same as what tablex does
aka measure(...)
so i'd say the consequences probably wouldnt be very significant
well, a consequence seems to be that the last auto row there is no longer zero-sized so it can violate the margin a bit
but idk if i would call that a "problem"
cuz here we explicitly made that group of rows unbreakable
it'd behave in the same fashion if it were a fixed size row instead of an auto one so
hey folks
look at what will be possible soon enough in your local 
" omg column headers are real?!?!?" well it's actually "manual" (you have to place the repetitions, you cant just tell the table 'repeat it for me') but otherwise yes!!! woo
full code:
#show table.cell.where(x: 0): strong
#show table.cell.where(y: 0): strong
#set page(height: 13em)
#let lets-repeat(thing, n) = ((thing + colbreak(),) * (calc.max(0, n - 1)) + (thing,)).join()
#table(
columns: 4,
fill: (x, y) => if x == 0 or y == 0 { gray },
[], [Test 1], [Test 2], [Test 3],
table.cell(rowspan: 15, align: horizon, lets-repeat((rotate(-90deg, reflow: true)[*All Tests*]), 3)),
..([123], [456], [789]) * 15
)
aight so,
#set page(width: 10cm, height: 2.5cm, margin: 0.5cm)
#set text(size: 11pt)
#table(
columns: (1fr, 1fr, 1fr),
[A],
[B],
[C],
[D],
table.cell(rowspan: 2, inset: (top: 0pt), lorem(4)),
[E],
[F],
[G],
)
renders "correctly" cuz you get 0pt top + 5pt bottom from above, it's definitely the frame of inset from the previous region somehow getting moved to the next region, i didnt know that was even possible
i'd call it a flow layout bug so , maybe not much rowspans can do here, the rowspan code seems to be all correct in this sense
Rowspans PR undrafted: https://github.com/typst/typst/pull/3501
Cc @robust viper - I've cleaned up a lot of stuff, but the core didnt change (at most got moved into separate functions in rowspans.rs as you requested)
you can take a quick look at things if you want , in particular you might benefit from taking another look at run_rowspan_simulation (the for loop of the algorithm), I made it perhaps more readable (gave better names to variables, used clearer syntax, added more comments and so on) without changing what it does
perhaps the most relevant change since your reviews (besides bug fixes) was that unbreakable auto rows now measure with infinite height
in principle this allows them to expand towards the margin, but i think we should consider this to be "working as intended"
the "weird spacing" bug you mentioned doesnt seem to be my fault (at least at first), seems to be some bug in the layout engine which is moving an empty inset frame from the previous page to the next (when that frame should just be deleted entirely)
You are right. I have debugged it and opened a fix: https://github.com/typst/typst/pull/3533
Ayy nice
I also debugged it for a bit and I was sure the problem was somewhere in there , but couldn't pinpoint the exact thing haha
So that's epic
Also btw
I think you made a typo in the release workflow fix PR
Or something
Cuz it seems to be interpreting vendor-openssl as a separate parameter instead of another --feature
Likely an easy fix though
I'll test a fix and report back soon tm
Does tm mean tomorrow?
No it means β’οΈ
oh yeah that's dumb of me
I had tested it in my fork previously but not after changing the feature flag ...
oh wow
i updated the rowspans branch to main and not only did the test case for the inset bug change (the table you sent), but also another test case
turns out the inset bug was already in one of my tests, i just didnt realize
lol
before vs after
well it's very zoomed in, thanks discord
lol
github actions is down
awesome
pull requests are also broken
my commits dont show up
yeah I noticed that while testing the PR
I wondered whether you had noticed that
the comments were also all gone last time I checked
it actually seems that that particular test was changed a few successive times after layout fixes lol
when i first made it the "a a" part was overflowing from the small segment at the top
and then i rebased the PR later and it just got magically fixed
lol
and then perhaps I noticed that it was a bit off but it felt like an improvement from before so maybe i just didnt care enough lol
ah here it is , the exact moment π haha
seems to be recovering now
actions are still a bit weird
but they run at least
okay well
RIP reviews then apparently
π
but it's ok
I believe I've addressed all of them
also btw i had a bit of a realization regarding headers
maybe that "problem" I was talking about isnt a real problem
maybe it's ok to reduce 100% depending on the available size
i'm gonna have to experiment with this :p
β οΈmaybe itβs ok to reduce 100% depending on available size β οΈ but then nothing was left.
haha i meant to reduce what 100% (the ratio) actually corresponds to
or, in other words, the total height available
Iβm just enjoying the thread. I saw some test results changed after your merge with main. (I observed from this thread) does this mean all of your previous issues are solved. ( wishful)
all that I know of, at least, yeah
header syntax just dropped
while testing before, i was using tablex-like header-rows: 2, cuz it's much easier to program that
lol
but now we have a proper thingβ’οΈ
headers are actually doing better than i anticipated
main functionality is basically done (for the MVP)
here MVP = a single header, repeating from top to bottom
in the future we'd add footers, and headers starting from arbitrary points in the table / footers ending at arbitrary points in the table
and even further into the future (as was suggested to me) we'd add header / footer levels (repeat parts of headers, change those parts over time)
cant wait to work on those
though theres one small problem
uni starts in 01 days
big oofer regarding available time so yeah
This is probably more than any one could hope for in a single release.
yep
but now i have made some major strides
I might have further news to share soonβ’οΈ ... π
wow
Yeah, it started as a "we need to make a style rework to fix some bugs", and become something so huge that I probably know only a fraction of what will be available after this release.
ladies and gentlemen...
PR is up! https://github.com/typst/typst/pull/3545
@robust viper please make sure to not check the PR diff as it is scary (it includes all of the rowspan diff)
the real diff is much smaller and simpler π
I had a blast with this haha
But it's finally ready π
@wary knot rowspans can merged, right?
yep
i added one extra commit today (backported from headers)
just a small little adjustment, nothing too important
queued for merge
woo-hoo π
with headers, we'll have basically completed the MVP π
tablex 2 is real π
IT IS A GREAT DAY FOR TABLES!
YESSSSS
my hands are actually shaking right now, this was such an epic journey
haha
I can't believe this.. you magnificent beast.
https://github.com/typst/typst/issues/3001 There are a few items you can check off here. You can just do it with your mouse.
MVP indeed
there you go π
Most-Valuable-Package
lets go
Thanks a lot, @wary knot You've put a lot of work and great care on this!
Better table support is probably the only thing in Typst that I really missed from LaTeX
You're welcome π«‘
Indeed I think this will be very important for Typst's adoption
At least for the use cases where tables shine
But you will also be able to use the new table features for layout purposes as well, the sky is the limit here
Haha
Anyway but yeah, I'm glad we got this far, and all of you were very important and valuable, even those who aren't in this thread
So thank you all as well π€π€
The feedback was fundamental, that's for sure
This is actually waaaay more impactful, beautiful and awesome than it looks like to begin with........
I'm working on touching up the docs examples for tables a bit.
Thinking that tables should have a guide. Users often want to create some. Adding a comprehensive section to the tutorial could be overwhelming while a terse reference does not have all the examples a user could want. An example of where the reference is not very useful right now is that it sends you off to the grid page for track definitions... So I think that a table guide / tutorial with more non-trivial examples is a good idea
@wary knot What do you think about these alternative examples for table (below the top example) ...
... and table.cell? They show the same principles but have been touched up a bit design-wise.
Will send the source if you want to integrate those
@midnight heron May I recommend adding the fact that you can import all the table.x stuff so that you don't always have to add the table in front if you use something often. At least I think that was possible, ignore if I am wrong about that lol
I'll also post some thoughts about the docs here as I go
- The grid docs and table docs should mutually explain when to use one and the other (namely, grid is for layout and table carries semantic meaning)
- Both functions take an
(x,y) => thingclosure for multiple arguments. I think the docs should explain that pattern explicitly somewhere - The "Styling the grid" section explains how
grid.cellis an override which is good, it should also mention how hline and vline override strokes - The same section contains an enumeration of the different levers to customize a grid in prose, maybe that should be a Markdown list
- Many grid examples should arguabely be tables. The grid reference should focus on the grid as a primitive to achieve layouts such as subfigures, complex callouts, and more
- The spread example is a bit lost between two styling-focussed examples
Yep that works, docs could mention it / make use of it
Yea just a small mention will do cause without it people won't assume it's possible and might complain about verbosity
- The
x.cellarguments that just saythe x overrideshould makexlink to the overridden argument hlineandvlineneed examples that demonstrate the behavior ofx/yandposition. The large paragraph there may otherwise be overwhelming- If you demonstrate one thing for tables in an example, you can link from
gridand say something like "You can find an example for use of thepositionargument in the table docs" and vice-versa.
the right side of emoji symbols are overflowed, which looks not great to me.
- An example for colspan or rowspan is missing
Both functions take an (x,y) => thing closure for multiple arguments. I think the docs should explain that pattern explicitly somewhere
Just noticed that you did that in the grid docs. Maybe something for the guide then.
I did that intentionally to demonstrate inset: 0pt as an override but we could also go with something else!
I'd suggest not stroking every edge here
For inset there probably should be (separate) an example that demonstrates its "real" use. But I don't really use it, so...
I think we should exhibit beautiful examples, since we will not use inset: 0pt if this is not perfect.
Yes, examples should only make people go "wow" and "cool". So inset (and other fields) should be used to make things pretty and not the other way around.
btw the trip process is funny and cool π.
I agree that examples should look good, but not every example needs to be "wow" because an example should also be focussed and not tweak every single property to its optimum point.
Rather, it should explain how to use one specific property, or a set of specific properties in combination in a terse way.
not tweak every single property to its optimum point.
Yes.
Another interpretation is to give "real world" examples of how to use specific stuff aaand it should look nice (with minimal tweaks) and show how user can improve their tables/documents.
I think that this example also must be included to documentation
Might be too complex for the reference, but could be good for a guide, maybe even explaining how to build it step by step
Although the question is whether the guide should have a progression like the tutorial or answer a bunch of questions like the page setup guide
In my experience, guides and tutorials are mostly the same thing and can overlap significantly if not fully.
If the guide will have a progression, then in order to accommodate several examples, it will obviously need to have several progressions. Perhaps a healthy mix of "step-by-step guide" and "to get A do B reference" would be ok.
For the second bullet point, it could be nice to have a more explicit function type to be able to display in the docs. i.e. notation (x: Int, y: Int) -> Content could be listed as one of the valid types, then it would give a specific place to discuss how that is valid. I know this is more tied up in the type rework, but would be nice to see this pattern. (and would be really useful for people familiar with functional programming to see this option explicitly)
And could use => as the arrow to be fancy and have the type match the code
they look nice for sure (bar the emoji overflow problem which was already mentioned - probably an easy fix)
-
agreed
-
agreed, could perhaps be in one of the fields
-
agreed, though I think line and stroke customization might demand its own subsection, e.g. explain how the stroke override order is grid
strokefield -> cellstrokefield -> hline/vline, and also mention the repetition of top and bottom border stroke which will have higher priority than conflicting cell strokes (also a repeated header's bottom stroke functions in a similar way, and so will footers in the future) -
perhaps; if it looks good then im ok with it
-
sure, I can get behind that, though I failed to find a really good example for it. I had thought of making a chess board kind of thing, but the chess emoji didnt look very good / overall I wasn't very satisfied with it, perhaps you can make the idea work though (I sent the source here #1182377294682128445 message )
overall I think we're in a good direction here for table docs, feel free to adjust what you think is needed, after all I had to focus on making tables look goodβ’οΈ rather than writing the best docs haha
i think this shouldnt be encouraged at least in principle, it can be mentioned but not necessarily used across examples or smth
cuz it's rare you will actually need to use a lot of the customization primitives
at least outside of a template
but when you have like a ton of table.cell calls then yeah i think it might make more sense
- I think it might be hard to click on an
xlink for some people, so not sure; perhaps the entire phrase "thexoverride" should be linked - fully agreed
- yep. So far we've been tending towards making
gridthe main "source of truth" so to speak, so table docs usually link more to grid docs rather than the opposite. But we should still have great examples on both.
PSA: using numbered list is better when you're discussing something, because the other party can easily reference any of the points by number which it always easier.
yea true, just didnt want to respond in a way that didnt match
that's what I'm talking about. if one starts using "unordered list", then responders kinda have to use it as well. And it doesn't look very readable (like here) (at least to me).
I tend to just use quote syntax when responding, which circumvents this problem.
Unless you need to reference the same point multiple times. Then you need to paraphrase and things like that.
https://github.com/typst/typst/pull/3545
I don't understand why there is no "disable headers" option. Are they must repeat if you use them?
I also don't get if all of these are gonna go to the next release or just the repeatable header and that's it.
if you read the PR, it specifies a repeat parameter which is true by default
and no, the intent isn't to have everything in that list ready for the next release. its just listed already, though Pt. 5a might still make it
Yea basically i thought you wouldn't need to use table.header if you wouldn't repeat it, but you can use repeat: false if you just want to use it for organizational purposes
Yeah, I skipped right through it, thanks.
So we are not sure what will make it to the next release and what is not? I assume repeated headers will be the last PR that is probably gonna make it?
basically whatever makes it through code review in time
realistically, i doubt @wary knot is sweating to get more out the door so close to release
I just think that @robust viper is waiting for @wary knot and vise versa for that last PR to be merged and then the "testing" phase will start. Unless the docs aren't finished yet.
From the #1176122103355953162 it feels like we either will have templates in the next release, or the work there is just being done "in parallel" and will not be included in the next release, as it is probably still WIP (from the looks of it).
It will be the last PR , yes
It is close to being merged i believe, im gonna make another commit today in response to a review and I think that'll be it, or almost it
Templates will be in 0.11. They are a bit last minute, but at the same time long overdue and I don't want to delay them for another few months.
I can't believe how huge this update will be. The margin between what I thought it would be and what it will actually be is unbelievable.
ok i've tackled all reviews i believe, i'm doing one last review pass at the PR now
later today i'll give my OK
and don't forget context!
ok so
I enabled my brain debugger (usually occurs involuntarily when I take a shower)
and i may have found 2 logic bugs that way
(not tested yet)
:p
they are small and easy to fix but it's kinda funny to think about
well i managed to reproduce one of them at least lol
You should probably refrain from computer usage while in the shower. Iβve been doing well not mixing the two.
lol
i wasnt being literal
by "brain debugger" i meant that i was "debugging my thoughts" by thinking further about them
What exactly is happening in the example?
basically this:
#set page(height: 10em)
#table(
columns: 2,
table.header(
[a], [b],
[c],
),
table.cell(x: 1, y: 1, rowspan: 2, lorem(80))
)
i included a cell in the header after the header was created
so the header didnt expand to include all of the cell's spanned rows
so the rowspan got repeated but the row under it didn't and it generated a huge mess
So can you fix it by adding the header later in the process. Or are you out of luck?
i fixed it already
So speedy.
in the fixup phase when replacing unspecified cells with empty cells, I also added a check
if the cell starts inside the header then make sure the header is at least as long as y + rowspan
anyway
theres still one bug left
it's not the one i was thinking of earlier
which is interesting
lol
Well one that we know about.
yeah
but i mean i dont want to stall the update with infinite tests
so i'll just work on fixing this one mostly
cuz it's rather important
like. theres something very fishy here
the rest of the table just vanished
lol
Hmm π€ had to go somewhere.
What happens when you break nested tables with headers across pages?
Thatβs a really satisfying result.
What if the nested table is the header. (I have not figured out what it should look like)
well , the header cant break across pages
Maybe infinite recuse
so it would just lead into a static nested table
Smart rule.
So you have xy coordinate placement. What happens when you place elements not at 0,0 say place elements at 5,5 and 7,7. I should probably just build main to see if I can find anything.
you mean #place?
Table.cell
you mean if you write table.header(table.cell(x: 5, y: 5)[something])?
Ohh that sounds dangerous
and (x: 7, y: 7) alongside it
well that particular case will just error
cuz then it wont start at the first row
the header has to start at y = 0
currently
but if u place something at the first row so it starts there then it should do exactly as you asked (the header goes all the way from rows 0 to 7 , and has some content at those two positions, the rest of cells are empty)
Nice. Well I couldnβt think of anything. Seems like a pretty nice system. What features are most likely to have anomalous behavior?
well, mostly arbitrary positioning of cells and rowspans
i fixed a bug with the former, now fixing a bug with the latter
Can a rowspan and a repeat header align in a bad way?
yeah, as seen here
it starts at the top and is cut
and then later resumed
but as if it still had stuff going on in the empty pages
hm wait
lol
nevermind what i said
thats not a rowspan
the rowspan is in the header
lol
anyway something it did screwed something up
I agree that the table is not perfect. But it does have character.
yeah
it's pretty cool already
im mostly hitting into edge cases here
lol
okay
i see
it's a problem in the header orphan check
basically it checks if a page would only have a header
if so, all rows are removed
but in this case it failed to check that properly
cuz a header row is empty and was removed
and it just checks row amount so yeah
should be easy fix
What gets filled ?
i mean, all the cells get respectively filled
no overlap in principle
ok the table is back
we are almost there
just gotta deal with this guy
that's not where they should be
will have to evict.
Ok what happens when you have knitted rows spans. And a header row. . Picks the longest. ( I forgot if this would be breakable ) a the the longest rowspan gets picked for repeat headers. And if I have all my cell colored blue then Iβd have some un filled gaps after that first repeat header.
yaaaay
okay
turns out my fix worked but
it was a bit of a hotfix
lol
cuz i actually found out the real problem
the rowspan in the header thought it started from the first page, but it clearly doesnt
so whatever i fixed wasnt supposed to be even run
very cool
now we have a proper solution
Puah the fix, and then go take another shower π€
kek
the ultimate secret
This was a reaction to your tables are real sentence. My father in law laser cut these custom coasters for me. Obviously, I'm a white beard kind of guy.
Now that's the real joke.
i felt like that was the case (regarding your first sentence)
a reference to my reference haha
Yay!!! Headers merged! https://github.com/typst/typst/pull/3545
Okay. Talk to me like I'm 5.. Isn't this like all? Also the stuff with tables that go beyond one page?
it's all for this release, yeah
but there are a few more ideas in the tables tracking issue
Uhm.. Which ones? I'll take a look.
I don't understand any of these ideas. But cool!
well
at least repeatable footers are easy to understand
it's like a header but on the bottom
regarding arbitrary positions, right now headers must be in the first row (and initially footers would have to end at the last row); the idea is to be able to place headers anywhere, such that they wouldnt be a header until they show up for the first time
But if I'm making simple ass tables, with headers being.... First row, then you've actually done that? Tables gave 90% feature set done?
the most common use cases were covered, if that's what you mean, yeah
That's a wild idea.
basically "sticky rows" sort of
once they're laid out once they stay
subheaders would take that to the limit: you'd be able to have multiple headers active at the same time
so you'd get a group of rows and it'd stay, then another group of rows and it'd stay below the previous one (cuz it has a higher "depth" / "level"), then you'd get another group of rows with the same level as the first so it'd replace the first one, and so on
While it sounds cool.. Can I ask, who asked for this? It sounds like Excel at this point.
itΕ mostly useful when you have something like
| Table |
| A | B | C | D |
and you just wanna change the "A B C D" part
Oh yeah.
That's a flattable in R.
Or ftable. Those are handy if you make a pivot / contingency table with multiple dimensions..... π
Alright, yeah those would be cool.
yea there are a few use cases
but let's not go too far for now
haha
i'll probably be making footers before that
at least, would be a greater gain in value
Automatically generates HTML variable documentation including variable names, labels, classes, value labels (if applicable), value ranges, and summary statistics. See the vignette "vtable" for a package overview.
Damn, I went from "what is this" to "when can I have this" pretty fast π€£ππ€¦ββοΈ
what im thinking of would have a more vertical structure
but yeah it's similar (changing only part of a header)
Awesome work. This isn't in Word I think...
One important thought: Can we make separate title / caption for a table?
Not that one that included in the figures!
First of all, figures can not properly show a miltipage table. Secondly, table may have different captions on different pages (like"Table 1. Beginning", "Table 1. Continuation", "Table. End")
while this is definitely something I intend to work on, last time I was asked about this I thought that it'd likely be better to improve figures instead of making this change specific to tables
we'd need to have some design discussion first however
btw @midnight heron it might be interesting to add an example like this too (but with repeated headers as well now probably)
source for that one is
#show table.cell.where(x: 0): strong
#show table.cell.where(y: 0): strong
#set page(height: 13em)
#let lets-repeat(thing, n) = ((thing + colbreak(),) * (calc.max(0, n - 1)) + (thing,)).join()
#table(
columns: 4,
fill: (x, y) => if x == 0 or y == 0 { gray },
[], [Test 1], [Test 2], [Test 3],
table.cell(rowspan: 15, align: horizon, lets-repeat((rotate(-90deg, reflow: true)[*All Tests*]), 3)),
..([123], [456], [789]) * 15
)
Very nice!
I like it when the examples contain some non-blind text. Makes it nicer to look at and the user's need to abstract less between the example and their use-case / emphasize more
With this example, you could put something like "USD per day" and then put "Blue chip", "Fresh IPO", "Penny stock" in the column headers
Yeah fair enough
Feel free to adjust it as needed for the docs as I'm running kinda low on creativity right now haha
Hey! I was wondering whether you have a docs PR in the works or whether we should edit them ourselves. Either is fine, but we need to know since it's one of the last things to do before .11 can enter testing.
unfortunately I don't, got kinda busy with uni now π¦
i might be able to help over the weekend though
if you can get something going for now that'd be great, and I can then give feedback and/or add further suggestions later
@midnight heron - let me know if I can help in any way - examples and such?
Okay!
We need some canonical examples for the new grid. The current docs on main contain primarily usages as a table replacement. We'd need usages that demonstrate how to use it for layouts instead. That way, users get nudged to use the semantically correct function for each.
It would be great if you could add some!
I think I should be able to help with line customization docs at least
the rules there are a bit intricate so maybe I should try to clarify them
in general grid stroke < cell stroke < hline / vline in terms of priority
but there are some edge cases and exceptions
e.g. top / bottom border, header line, conflict between two things of same priority (=> cell below / to the right wins, hline / vline specified later wins)
so it's probably best to have that at least documented somewhere
the rest should be relatively simple to write docs for (colspan / rowspan, headers)
Btw @midnight heron i had an idea for a grid layout example
i think we could demonstrate the usage of colspans and rowspans to make a fully customized page layout with grid
For example, you'd use a rowspan to make a "sidebar" of sorts, and a colspan to make some header stuff
Though i think repeatable headers are mostly useful for tables , at least while they're in MVP stage (only at the first row for now)
We could also demonstrate line customization this way, e.g. to add a line under the header or smth of the sort
Can you send me a quick sketch of how that would look like?
Yeah sure, I'll send it later today once I'm at my PC
to clarify a bit for now though, i think my idea would have some applicability in CVs for example , where you often need to pack a lot of information into one page, so having colspans and rowspans gives you lots of flexibility in that regard , i believe
Which is something you'd otherwise have to use nested grids for
i was also thinking in a similar direction, namely slide layouts. when a slide spans multiple pages (like a bibliography) repeatable grid headers (and footers) would work well for slide headers/footers
heres more or less what I was thinking regarding the CV idea
#set page(height: 16em)
#grid(
columns: 2,
inset: 5pt,
stroke: (x, y) => if x == 0 and y > 0 { (right: (paint: luma(180), thickness: 1.5pt, dash: "dotted")) },
grid.header(
grid.cell(colspan: 2)[*Previous Experience* #box(width: 1fr, line(length: 100%, stroke: luma(180)))]
),
grid.cell(rowspan: 2)[*2012*],
[*Pear Seed & Co.* - Lead Engineer _(Mar - Jun)_
- Did a great job
- Helped the company grow],
[*Mega Corp.* - VP of Sales _(Jul - Dec)_
- Raised a lot of funds],
[*2013*], [*Tiny Co.* - CEO _(Jan - Dec)_
- Had lots of clients],
[*2014*], [*Medium Co.* - CTO _(Jan - Mar)_
- Did a lot of work]
)
though my initial idea was a bit more general than that
like using grid to separate the different parts of a page (header (colspan), sidebar / aside (rowspans), body (one or more regular cells), footer (colspan))
this particular example would benefit from "repeatable rowspans" / column headers if we reduce the page height, but we can still use the colbreak workaround
with 15em page height and [*2013* #colbreak() *2013*]
actually i think maybe posters could benefit from that as well
hmm
alright
now that's closer to what I was thinking of (although I also like the previous example)
a page done entirely using grid
here's the code
#set page(height: auto, margin: 0pt)
#let title(it) = strong(smallcaps(text(32pt, it)))
#let subtitle(it) = emph(text(20pt, it))
#let titlebar(it, ..block-args) = text(white, block(width: 100%, fill: blue, inset: 10pt, ..block-args, align(center, it)))
#let titled-cell(title, body, ..block-args) = stack(dir: ttb, titlebar(title, ..block-args), block(inset: (top: 5pt), body))
#grid(
columns: (1fr, 1fr, 1fr),
inset: 10pt,
grid.header(
grid.cell(
colspan: 3,
titlebar(fill: green.darken(50%))[#title[Our Epic Project]\ #subtitle[A study on plants]]
)
),
grid.cell(
colspan: 2,
titled-cell[*Our Origin*][We come from a small town in our country.],
),
titled-cell[*Our Team*][We are Bob, John, Kelly and Nate.],
grid.cell(rowspan: 2, titled-cell[*Our Story*][#lorem(50)]),
titled-cell[*Our Goals*][
- We seek a better future.
- We seek to learn more.
- We seek to push the boundaries of knowledge.
],
titled-cell[*Our Values*][We are always very positive.],
grid.cell(colspan: 2, titled-cell[*Contact Information*][
- You can find us on social media; our handle is ThePlantsGroup.
- You can shoot us an email: #underline(text(blue)[email\@doesnt.exist])]),
grid.cell(colspan: 3, titlebar(fill: orange.darken(20%), title[Join us now!]))
)
i feel like the code is pretty simple even
compared to what it could need to be today
with html export and this, typst will truly be reinventing table-based layouts on the web
so anyway folks
i was a bit bored and you know
it was a bit tempting so uh
uh
drops and runs away
Ah the footer thing
im feeling brave https://github.com/typst/typst/pull/3577
Adds table.footer and grid.footer to have some rows repeat at the bottom of a table's pages.
First part of the sixth task in #3001.
Currently, footers must always include the last row of the ta...
π
I swear I had the best intentions @robust viper β€οΈ
π
though i like how i barely had to change anything in rowspans.rs
nobody likes debugging rowspans for +inf hours
if headers were simpler than rowspans, footers are even simpler than headers haha
Man this was so insane i actually wrote "rowspan tests" instead of "footer tests" in one of the commits lol
Im gonna catch some sleep ASAP π
It seems to me that in typst 0.11 we will have the entire set of capabilities for the table, because:
-
The typst team is trying to write documentation for the available features
-
@wary knot manages to prepare a new PR during this time
-
See item 1. π€£
Nah i will stop for real now lol
I could only do this cuz footers use the same infrastructure for headers
So it was very fast
I literally did it in the past few hours
But anything more complicated than that would have been impossible to do that way :p
The footer thing makes the use case with polylux fully possible now π
This looks great!
First part of the sixth task in #3001.
Those docs better be outstanding, because it is a bit tricky to parse.. So the repeatable footer can be a collection of rows.
I didn't have time to write too much :p but yes
The examples have two rows as header and footer
Yeah.. Actually it makes perfect sense. Sorry..
It's ok
shh don't stop him. (tables! tables!)
ok
ive done several review passes, things are looking good in general
later today i'll probably add some extra tests just in case and that'll be it
π
@wary knot since footers are so similar to headers, cloning the full header test suite (including yet again 400KB of images) feels excessive to me. can't we validate that it behaves mostly the same with a smaller, more focused test suite? while generally, having more tests is a good thing, it can also become a maintenance burden.
I was thinking of unifying the two
Just don't know the best way to do it ATM
But I think many of those tests end up testing the same thing for headers and footers so it should be ok to just use the tests for both purposes
I've minified the tests myself a bit now since I wasn't sure whether you'd be online again today
I've pushed to the PR
I will merge now
no worries
either way we can always simplify things further later if needed so should be ok
I want to get rid of the reference images anyway
I just want SHA of the image bytes really
For the tests themselves it isn't a problem, just the dev & review experience will need some solution
i think you did a good job in shortening the tests actually
i was a bit worried about how you removed the gutter test
but another test had it so
either way yeah we can improve things further in the future
I tried my best to retain the intent, though it was a bit hard for some of the tests
yea i think it's fine
okay
we can now announce
Footers merged!! π https://github.com/typst/typst/pull/3577
We can all agree that this update is gonna be Very Epicβ’οΈ
Biiig changelog
and therefore the world
Do we have a simple (<15 short lines) example in mind for table footers?
Otherwise typst/typst#3593 is ready to merge
seems like there are some examples online with sum on footers
Then you'd have a totals on one page, that doesn't reflect whats visible, but the total on both current, and next page.
yeah true
i guess the example does work for non-repeated, purely semantical footers though
but that's indeed a concern
You ought to see something with an example, which is hard because show set rules for headers and footers do not yet work
yeah we'd need to adapt the styling system a bit for that to work i think
Ah, just write a Source note. These numbers are accreditied to some institution or something.
https://github.com/typst/typst/pull/3577/files#diff-47e426f55408c9a96fa2ba60c6f259b5b2abba2cc42247528fd2aea0bed211ff
Is there supposed/necessary to be an empty page in the tests? I don't think I saw that previously.
yes
read the test title
it's basically what it's testing
the header shouldnt be an orphan in the page
so it is sent to the next page
oh, I see.
Which colbreak trick?
Ah, the one where it douplicates the label?
I removed it because its a bit hacky and assumes knowlege of how many pages the table breaks on to
hmm
yea that's true, though I just wanted to let people know it's possible
might be a common question
but in the future we'll certainly have a proper way to do this
i think we can leave it as is for now, and then in the next update we add a proper "repeatable rowspan" mechanism π
oh hey
the chess example was added
huzzah
Can you do a continued on next page example. It would make me feel happy π.
well, there's no native support for that yet
but you might be able to hack something with footers
ok here you go
#set page(height: 8em)
#let page-counter = counter("table-counter")
#table(
columns: 3,
..([a], [b], [c],) * 5,
table.footer(
table.cell(
colspan: 3,
stroke: none,
context {
let this-page = here().page()
if this-page != page-counter.final().first() {
[Next page!]
}
page-counter.step()
}
)
)
)
But this would only work if the table ends at the last page of the document. Or I'm dumb...
Ok, this is actually genius. I didn't read the page-counter.step() line.
Wait, no, there is a different flaw. It only works if the table starts at the beginning of the document.
If it were to start at the page 10, then the counter would go from 0 to 5 and the page would go from 10 to 12. So the condition will always be true.
ah right
hold on lol
ok
#set page(height: 8em)
#let page-counter = counter("table-counter")
#show table: it => {
context {
let this-page = here().page()
page-counter.update(this-page - 1)
}
it
}
#table(
columns: 3,
..([a], [b], [c],) * 5,
table.footer(
table.cell(
colspan: 3,
stroke: none,
context {
let this-page = here().page()
if this-page != page-counter.final().first() {
[Next page!]
}
page-counter.step()
}
)
)
)
there we go
wont work very well with nested tables
actually i can fix that
You need to compare to page-conter.get().
wth...
hmm
yeah i think you're right
it will still have problems
with multiple tables
we need a way to generate unique keys for counters
lol
#set page(height: 8em)
first page
#pagebreak()
#let page-counter = counter("table-counter")
#table(
columns: 3,
..([a], [b], [c],) * 6,
table.footer(
table.cell(
colspan: 3,
stroke: none,
{
page-counter.step()
context {
if page-counter.get() != page-counter.final() {
[Next page!]
}
}
},
),
),
)
#pagebreak()
last page
The only obvious problem is that this counter is single-use only. You should use a new one or reset this one for each new table use.
okay
i have a solution
#set page(height: 8em)
#let page-counter-counter = counter("yak-is-being-shaved")
#show table: it => context {
page-counter-counter.step()
let page-turn-counter = counter("table-page-turn-counter-" + str(page-counter-counter.get().first()))
page-turn-counter.update(here().page() - 1)
show <on-page-turn>: it => context {
if here().page() != page-turn-counter.final().first() {
it
}
page-turn-counter.step()
}
it
}
#table(
columns: 3,
..([a], [b], [c],) * 5,
table.footer(
table.cell(
colspan: 3,
stroke: none,
[#[Next page!] <on-page-turn>]
)
)
)
adding more tables works
I would like this hack to not require a global-scope custom counter, because it will feel and look more of a hack than a proper solution. Does the same-name counter persist if is called from different local scopes?
yeah it persists
of note, tablex uses something like this
lol
a counter for counters
So then the counter(s) should be put in the show rule as well.
the second global counter there is unnecessary
so i removed it
ok ive moved the counter to inside the show rule too
personally i prefer keeping it global
but that will work
#set page(height: 8em)
#show table: it => context {
let page-counter-counter = counter("yak-is-being-shaved")
page-counter-counter.step()
let page-turn-counter = counter("table-page-turn-counter-" + str(page-counter-counter.get().first()))
page-turn-counter.update(here().page() - 1)
show <on-page-turn>: it => context {
// Only display this content if we're not at the last page of the table
if here().page() != page-turn-counter.final().first() {
it
}
page-turn-counter.step()
}
it
}
#table(
columns: 3,
..([a], [b], [c],) * 5,
table.footer(
table.cell(
colspan: 3,
stroke: none,
[#[Next page!] <on-page-turn>]
)
)
)
final solution
also darn context is so ergonomic to use lol
Bro, What I said about is literally more sane even with double counters:
#set page(height: 8em)
#show table: it => context {
let table-counter = counter("table")
table-counter.step()
let table-part-counter = counter("table-part" + str(table-counter.get().first()))
show <table-footer>: footer => {
table-part-counter.step()
context {
if table-part-counter.get() != table-part-counter.final() {
footer
}
}
}
it
}
first page
#pagebreak()
#table(
columns: 3,
..([a], [b], [c],) * 6,
table.footer(
table.cell(
colspan: 3,
stroke: none,
[#[Next page!] <table-footer>]
)
)
)
#pagebreak()
last page
and you should've used readable names, because I was lost there until I tried debugging step by step.
lol sorry
but yeah you're right, that way works cuz the footer only appears once for each page so yeah
huzzah
The only thing left to do is to remove the label so that you can define the footer normally. This would be as clean as a normal code, but with one smart show rule.
yeah unfortunately show rules on table.footer wont work
i wanna make it work but it'd need some coordination with the rest of styling system
in particular there's no show-show so that's even harder
show-set is something that is much easier to make work
But I tried the hackable hack and of course you can't mutate read-only variable and children on elements and of course you would hit show rule recursion if you are to recreate the table from its fields/children.
yeah i think a better approach would be a predefined footer
Oh, right, there is no point in recreating the table, since the table itself isn't split into multiple callbacks. So it wouldn't work either way.
also theres one shortcoming with this approach: footers have the same height across all pages
In what way? But predefined means it would be clean?
so there will be some blank space at the end of the table
not that bad though, some negative vertical space solves it
something like #let next-page-footer(columns) = table.footer(colspan: columns, stroke: none, [#[Stuff] <hide-me-at-last-page>])
then you'd just write #next-page-footer(5)
This can be solved with:
if table-part-counter.get() != table-part-counter.final() {
show table.footer: set table.cell(inset: 0pt)
footer
}
But the problem is it doesn't work. Adding it directly does:
table.footer(
table.cell(
colspan: 3,
inset: 0pt,
[#[Next page!] <table-footer>]
)
)
But now all footers are affected.
that wont work for two reasons, the first one is that i simply dont check for show set rules for headers and footers anywhere in the code so yea
the second is that youre already inside the footer, so a show rule on footers wont apply inside the footer, unless theres a nested table in the footer
Hmm, yeah. This already looks clean:
#set page(height: 8em)
#let table-footer(repeat: true, ..cell-args) = table.footer(
table.cell(
..cell-args.named(),
[#cell-args.pos().first() <table-footer>]
)
)
#show table: it => context {
let table-counter = counter("table")
table-counter.step()
let table-part-counter = counter("table-part" + str(table-counter.get().first()))
show <table-footer>: footer-content => {
table-part-counter.step()
context {
if table-part-counter.get() != table-part-counter.final() {
footer-content
}
}
}
it
}
first page
#pagebreak()
#table(
columns: 3,
..([a], [b], [c],) * 6,
table-footer(colspan: 3, [Next page!])
)
#pagebreak()
last page
There is a quirk that now footer also gets all the cell arguments, but at least it's even more clean than using footer(cell()).
I also marked the label's element as footer-content, because it's not a footer and therefore you can't apply inset set rule.
ok
this chat is being heavily spammed lol
but here's a fully integrated solution
#set page(height: 8em)
#let next-page-table(next-page-content: [], ..table-args) = context {
let columns = table-args.named().at("columns", default: 1)
let column-amount = if type(columns) == int {
columns
} else if type(columns) == array {
columns.len()
} else {
1
}
// Counter of tables so we can create a unique table-part-counter for each table
let table-counter = counter("table")
table-counter.step()
// Counter for the amount of pages in the table
// It is increased by one for each footer repetition
let table-part-counter = counter("table-part" + str(table-counter.get().first()))
show <table-footer>: footer => {
table-part-counter.step()
context {
if table-part-counter.get() != table-part-counter.final() {
// Display the footer only if we aren't at the last page
footer
}
}
}
table(
..table-args,
table.footer(
// The 'next page' content spans all columns and has no stroke
// Must be selectable by the show rule above which hides it at the last page
table.cell(colspan: column-amount, stroke: none, [#next-page-content <table-footer>])
)
)
// Compensate for the empty footer at the last page of the table
v(-measure(next-page-content).height)
}
first page
#pagebreak()
#next-page-table(
next-page-content: [Next page!],
columns: 3,
..([a], [b], [c],) * 6,
)
f
#pagebreak()
last page
also compensates for the empty footer space at the last page
I'm pretty sure it wouldn't work when stroke is normal, i.e., not none. So I made a rect as a table.cell (inside table.cell) so that you can do all the regular cell stuff, but if it is the last page, then there will be literally nothing. But it also adds complexity in passing different arguments to different functions.
#set page(height: 8em)
#set table.cell(stroke: green)
#let table-footer(repeat: true, stroke: auto, ..cell-args) = {
table.footer(
table.cell(
..cell-args.named(),
stroke: none,
// table.cell's default inset: 5pt
context pad(-5pt, rect(
// probably stroke is not the only field that should be processed
stroke: if stroke == auto {
if table.cell.stroke == (:) { auto } else { table.cell.stroke }
} else { stroke }
)[#cell-args.pos().first() <table-footer>]),
)
)
}
#show table: it => context {
let table-counter = counter("table")
table-counter.step()
let table-part-counter = counter("table-part" + str(table-counter.get().first()))
show <table-footer>: footer-content => {
table-part-counter.step()
context {
if table-part-counter.get() != table-part-counter.final() {
footer-content
}
else {
v(-5pt * 2)
}
}
}
it
}
first page
#pagebreak()
#table(
columns: 3,
..([a], [b], [c],) * 6,
table-footer(colspan: 3, stroke: red, [Next page!])
)
#pagebreak()
last page
So here global green works as expected, local red also works and none also works. So basically the rect works transparently for the user. At least with stroke. But you definitely can finish the argument parsing, and it probably will "just work".
If only discord could support, oh, I don't know, a <details> block?
But at least in the future, everyone can refer to our examples. So it's spam for the good.
i mean the cell stroke has to be none
why would it not be?
you dont want to add anything around the caption
with my example it works just fine with global stroke override for e.g.
Because it also overrides the in-footer cell's stroke.
I mean, if the footer must have no stroke, then sure.
not if you specify table.cell(stroke: none)
oh, right
But if it does have a stroke, then specifying a footer's stroke wouldn't work nicely in your example, but does work in mine.
Try also setting the global stroke to green and local (footer) stroke to red.
You should have a stoke-width red line at the bottom of the table. Which isn't desired.
my point was mostly that theres no reason to do that if you want a caption
usually you dont add lines around captions
But I didn't have "caption" in mind.
yea but iiuc "table continues in the next page" is always a caption
this is mostly a workaround for it
Will this "caption" work if it is wider than 3 cells?
But then you would have a fake caption. What about the real one?
yeah you'd remove the real one
well, or keep it and have it appear at the last page instead
the idea is that in the future figure would support repeatable captions natively
We have a caption for each part. which only differs in one or two words.
so we're mostly just hacking around it rn
Oh, yeeah, I like the sound of this.
But will they be customizable?
I need the first caption to be the normal and the next captions to have a different prefix/supplement, IIRC. But maybe also remove the separator and body ("Continuation of table 1"). One (or both) of them is definitely correct.
Made my day. π
i'd argue that the first table example on the table reference page and the example in the readme of Typst's repository should make use of the new stroke customisation features and get rid of this suboptimal full grid of lines.
theres also something odd here
the output doesnt match the code
yea it's hidden
cc @midnight heron i'd argue that this set rule should be exposed (or even inlined)
we definitely need at least one example without vertical lines
Maybe make it collapsible? (probably it's planned). Like in mdbook, you can hide parts of code, and show it by clicking on ποΈ
emph is not applied
Yeah thatβs because the italic IBM Plex font is not in typst-dev-assets
The purpose of this example was the colspan, which is why we decided that the rest is just noise and "part of the template" so to speak.
Having fancy-looking tables in the examples requires a fair bit of code every time, which takes away the focus of each example.
Suggestion: Link or toggle to see the full example?
Perhaps repeat should be false by default in footers π€
well, it's not really the point of the feature
headers and footers were mostly designed with repetition in mind
i'd say that it's better to have the user know that the feature is available, and disable if needed
you can also disable globally with #set table.footer(repeat: false)
(show rules wont work tho)
we need a query(table::after)
wdym? fetch what's immediately after the table?
okay fair enough it's a bit weirdly put like that
but like element.loc() except for its end
yeah fair enough, though in that particular case i guess it doesnt have to be "fancy"