diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs index b58b50549..dc16c3c39 100644 --- a/crates/typst/src/layout/grid/mod.rs +++ b/crates/typst/src/layout/grid/mod.rs @@ -36,9 +36,19 @@ use crate::visualize::{Paint, Stroke}; /// There are multiple sizing modes for columns and rows that can be used to /// create complex layouts. /// -/// The sizing of the grid is determined by the track sizes specified in the -/// arguments. Because each of the sizing parameters accepts the same values, we -/// will explain them just once, here. Each sizing argument accepts an array of +/// While the grid and table elements work very similarly, they are intended for +/// different use cases and carry different semantics. The grid element is +/// intended for presentational and layout purposes, while the +/// [`{table}`]($table) element is intended for, in broad terms, presenting +/// multiple related data points. In the future, Typst will annotate its output +/// such that screenreaders will annouce content in `table` as tabular while a +/// grid's content will be announced no different than multiple content blocks +/// in the document flow. Set and show rules on one of these elements do not +/// affect the other. +/// +/// A grid's sizing is determined by the track sizes specified in the arguments. +/// Because each of the sizing parameters accepts the same values, we will +/// explain them just once, here. Each sizing argument accepts an array of /// individual track sizes. A track size is either: /// /// - `{auto}`: The track will be sized to fit its contents. It will be at most @@ -60,22 +70,10 @@ use crate::visualize::{Paint, Stroke}; /// instead of an array. For example, `columns:` `{3}` is equivalent to /// `columns:` `{(auto, auto, auto)}`. /// -/// # Styling the grid -/// The grid's appearance can be customized through different parameters, such -/// as `fill` to give all cells a background; `align` to change how cells are -/// aligned; `inset` to optionally add internal padding to each cell; and -/// `stroke` to optionally enable grid lines with a certain stroke. -/// -/// If you need to override one of the above options for a single cell, you can -/// use the [`grid.cell`]($grid.cell) element. Alternatively, if you need the -/// appearance options to depend on a cell's position (column and row), you may -/// specify a function to `fill` or `align` of the form -/// `(column, row) => value`. You may also use a show rule on -/// [`grid.cell`]($grid.cell) - see that element's examples or the examples -/// below for more information. -/// /// # Examples -/// The example below demonstrates the different track sizing options. +/// The example below demonstrates the different track sizing options. It also +/// shows how you can use [`grid.cell`]($grid.cell) in to make an individual +/// cell span two grid tracks. /// /// ```example /// // We use `rect` to emphasize the @@ -94,8 +92,10 @@ use crate::visualize::{Paint, Stroke}; /// rect[1/3 of the remains], /// rect[2/3 of the remains], /// rect(height: 100%)[Fixed height], -/// image("tiger.jpg", height: 100%), -/// image("tiger.jpg", height: 100%), +/// grid.cell( +/// colspan: 2, +/// image("tiger.jpg", width: 100%), +/// ), /// ) /// ``` /// @@ -110,60 +110,42 @@ use crate::visualize::{Paint, Stroke}; /// ) /// ``` /// -/// Additionally, you can use [`grid.cell`]($grid.cell) in various ways to -/// not only style each cell based on its position and other fields, but also -/// to determine the cell's preferential position in the table. +/// # Styling the grid +/// The grid's appearance can be customized through different parameters. These +/// are the most important ones: /// -/// ```example -/// #set page(width: auto) -/// #show grid.cell: it => { -/// if it.y == 0 { -/// // The first row's text must be white and bold. -/// set text(white) -/// strong(it) -/// } else { -/// // For the second row and beyond, we will show the day number for each -/// // cell. +/// - [`fill`]($grid.fill) to give all cells a background +/// - [`align`]($grid.align) to change how cells are aligned +/// - [`inset`]($grid.inset) to optionally add internal padding to each cell +/// - [`stroke`]($grid.stroke) to optionally enable grid lines with a certain +/// stroke /// -/// // In general, a cell's index is given by cell.x + columns * cell.y. -/// // Days start in the second grid row, so we subtract 1 row. -/// // But the first day is day 1, not day 0, so we add 1. -/// let day = it.x + 7 * (it.y - 1) + 1 -/// if day <= 31 { -/// // Place the day's number at the top left of the cell. -/// // Only if the day is valid for this month (not 32 or higher). -/// place(top + left, dx: 2pt, dy: 2pt, text(8pt, red.darken(40%))[#day]) -/// } -/// it -/// } -/// } +/// If you need to override one of the above options for a single cell, you can +/// use the [`grid.cell`]($grid.cell) element. Likewise, you can override +/// individual grid lines with the [`grid.hline`]($grid.hline) and +/// [`grid.vline`]($grid.vline) elements. /// -/// #grid( -/// fill: (x, y) => if y == 0 { gray.darken(50%) }, -/// columns: (30pt,) * 7, -/// rows: (auto, 30pt), -/// // Events will be written at the bottom of each day square. -/// align: bottom, -/// inset: 5pt, -/// stroke: (thickness: 0.5pt, dash: "densely-dotted"), +/// Alternatively, if you need the appearance options to depend on a cell's +/// position (column and row), you may specify a function to `fill` or `align` +/// of the form `(column, row) => value`. You may also use a show rule on +/// [`grid.cell`]($grid.cell) - see that element's examples or the examples +/// below for more information. /// -/// [Sun], [Mon], [Tue], [Wed], [Thu], [Fri], [Sat], +/// Locating most of your styling in set and show rules is recommended, as it +/// keeps the grid's or table's actual usages clean and easy to read. It also +/// allows you to easily change the grid's appearance in one place. /// -/// // This event will occur on the first Friday (sixth column). -/// grid.cell(x: 5, fill: yellow.darken(10%))[Call], +/// ## Stroke styling precedence +/// There are three ways to set the stroke of a grid cell: through +/// [`{grid.cell}`'s `stroke` field]($grid.cell.stroke), by using +/// [`{grid.hline}`]($grid.hline) and [`{grid.vline}`]($grid.vline), or by +/// setting the [`{grid}`'s `stroke` field]($grid.stroke). When multiple of +/// these settings are present and conflict, the `hline` and `vline` settings +/// take the highest precedence, followed by the `cell` settings, and finally +/// the `grid` settings. /// -/// // This event will occur every Monday (second column). -/// // We have to repeat it 5 times so it occurs every week. -/// ..(grid.cell(x: 1, fill: red.lighten(50%))[Meet],) * 5, -/// -/// // This event will occur at day 19. -/// grid.cell(x: 4, y: 3, fill: orange.lighten(25%))[Talk], -/// -/// // These events will occur at the second week, where available. -/// grid.cell(y: 2, fill: aqua)[Chat], -/// grid.cell(y: 2, fill: aqua)[Walk], -/// ) -/// ``` +/// Furthermore, strokes of a repeated grid header or footer will take +/// precedence over regular cell strokes. #[elem(scope, LayoutMultiple)] pub struct GridElem { /// The column sizes. @@ -209,13 +191,16 @@ pub struct GridElem { /// /// ```example /// #grid( - /// fill: (col, row) => if calc.even(col + row) { luma(240) } else { white }, + /// fill: (col, row) => + /// if calc.even(col + row) { luma(230) } + /// else { white }, /// align: center + horizon, /// columns: 4, + /// inset: 2pt, /// [X], [O], [X], [O], /// [O], [X], [O], [X], /// [X], [O], [X], [O], - /// [O], [X], [O], [X] + /// [O], [X], [O], [X], /// ) /// ``` #[borrowed] @@ -225,17 +210,11 @@ pub struct GridElem { /// /// This can either be a single alignment, an array of alignments /// (corresponding to each column) or a function that returns an alignment. - /// The function is passed the cells' column and row index, starting at zero. - /// If set to `{auto}`, the outer alignment is used. + /// The function is passed the cells' column and row index, starting at + /// zero. If set to `{auto}`, the outer alignment is used. /// - /// ```example - /// #grid( - /// columns: 3, - /// align: (x, y) => (left, center, right).at(x), - /// [Hello], [Hello], [Hello], - /// [A], [B], [C], - /// ) - /// ``` + /// You can find an example for this argument at the + /// [`table.align`]($table.align) parameter. #[borrowed] pub align: Celled>, @@ -249,31 +228,81 @@ pub struct GridElem { /// multiple specific cells, consider specifying one or more of /// [`grid.hline`]($grid.hline) and [`grid.vline`]($grid.vline) alongside /// your grid cells. + /// + /// ```example + /// #set page(height: 13em, width: 26em) + /// + /// #let cv(..jobs) = 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)[ + /// *Professional Experience* + /// #box(width: 1fr, line(length: 100%, stroke: luma(180))) + /// ]), + /// ..{ + /// let last = none + /// for job in jobs.pos() { + /// ( + /// if job.year != last [*#job.year*], + /// [ + /// *#job.company* - #job.role _(#job.timeframe)_ \ + /// #job.details + /// ] + /// ) + /// last = job.year + /// } + /// } + /// ) + /// + /// #cv( + /// ( + /// year: 2012, + /// company: [Pear Seed & Co.], + /// role: [Lead Engineer], + /// timeframe: [Jul - Dec], + /// details: [ + /// - Raised engineers from 3x to 10x + /// - Did a great job + /// ], + /// ), + /// ( + /// year: 2012, + /// company: [Mega Corp.], + /// role: [VP of Sales], + /// timeframe: [Mar - Jun], + /// details: [- Closed tons of customers], + /// ), + /// ( + /// year: 2013, + /// company: [Tiny Co.], + /// role: [CEO], + /// timeframe: [Jan - Dec], + /// details: [- Delivered 4x more shareholder value], + /// ), + /// ( + /// year: 2014, + /// company: [Glorbocorp Ltd], + /// role: [CTO], + /// timeframe: [Jan - Mar], + /// details: [- Drove containerization forward], + /// ), + /// ) + /// ``` #[resolve] #[fold] pub stroke: Celled>>>>, /// How much to pad the cells' content. /// - /// ```example - /// #grid( - /// inset: 10pt, - /// fill: (_, row) => (red, blue).at(row), - /// [Hello], - /// [World], - /// ) - /// - /// #grid( - /// columns: 2, - /// inset: ( - /// x: 20pt, - /// y: 10pt, - /// ), - /// fill: (col, _) => (red, blue).at(col), - /// [Hello], - /// [World], - /// ) - /// ``` + /// You can find an example for this argument at the + /// [`table.inset`]($table.inset) parameter. #[fold] pub inset: Celled>>>, @@ -508,6 +537,10 @@ impl TryFrom for GridItem { } /// A repeatable grid header. +/// +/// If `repeat` is set to `true`, the header will be repeated across pages. For +/// an example, refer to the [`table.header`]($table.header) element and the +/// [`grid.stroke`]($grid.stroke) parameter. #[elem(name = "header", title = "Grid Header")] pub struct GridHeader { /// Whether this header should be repeated across pages. @@ -520,6 +553,11 @@ pub struct GridHeader { } /// A repeatable grid footer. +/// +/// Just like the [`grid.header`]($grid.header) element, the footer can repeat +/// itself on every page of the table. +/// +/// No other grid cells may be placed after the footer. #[elem(name = "footer", title = "Grid Footer")] pub struct GridFooter { /// Whether this footer should be repeated across pages. @@ -533,9 +571,12 @@ pub struct GridFooter { /// A horizontal line in the grid. /// -/// Overrides any per-cell stroke, including stroke specified through the -/// grid's `stroke` field. Can cross spacing between cells created through -/// the grid's `column-gutter` option. +/// Overrides any per-cell stroke, including stroke specified through the grid's +/// `stroke` field. Can cross spacing between cells created through the grid's +/// `column-gutter` option. +/// +/// An example for this function can be found at the +/// [`table.hline`]($table.hline) element. #[elem(name = "hline", title = "Grid Horizontal Line")] pub struct GridHLine { /// The row above which the horizontal line is placed (zero-indexed). @@ -644,53 +685,46 @@ pub struct GridVLine { pub position: OuterHAlignment, } -/// A cell in the grid. Use this to either override grid properties for a -/// particular cell, or in show rules to apply certain styles to multiple cells -/// at once. +/// A cell in the grid. You can use this function in the argument list of a grid +/// to override grid style properties for an individual cell or manually +/// positioning it within the grid. You can also use this function in show rules +/// to apply certain styles to multiple cells at once. /// -/// For example, you can override the fill, alignment or inset for a single -/// cell: +/// For example, you can override the position and stroke for a single cell: /// /// ```example +/// >>> #set page(width: auto) +/// >>> #set text(15pt, font: "Noto Sans Symbols 2", bottom-edge: -.2em) +/// <<< #set text(15pt, font: "Noto Sans Symbols 2") +/// #show regex("[♚-♟︎]"): set text(fill: rgb("21212A")) +/// #show regex("[♔-♙]"): set text(fill: rgb("111015")) +/// /// #grid( -/// columns: 2, -/// fill: red, -/// align: left, -/// inset: 5pt, -/// [ABC], [ABC], -/// grid.cell(fill: blue)[C], [D], -/// grid.cell(align: center)[E], [F], -/// [G], grid.cell(inset: 0pt)[H] +/// fill: (x, y) => rgb( +/// if calc.odd(x + y) { "EFF0F3" } +/// else { "7F8396" } +/// ), +/// columns: (1em,) * 8, +/// rows: 1em, +/// align: center + horizon, +/// +/// [♜], [♞], [♝], [♛], [♚], [♝], [♞], [♜], +/// [♟], [♟], [♟], [♟], [], [♟], [♟], [♟], +/// grid.cell( +/// x: 4, y: 3, +/// stroke: blue.transparentize(60%) +/// )[♟], +/// +/// ..(grid.cell(y: 6)[♙],) * 8, +/// ..([♖], [♘], [♗], [♕], [♔], [♗], [♘], [♖]) +/// .map(grid.cell.with(y: 7)), /// ) /// ``` /// /// You may also apply a show rule on `grid.cell` to style all cells at once, -/// which allows you, for example, to apply styles based on a cell's position: -/// -/// ```example -/// #show grid.cell: it => { -/// if it.y == 0 { -/// // First row is bold -/// strong(it) -/// } else if it.x == 1 { -/// // Second column is italicized -/// // (except at the first row) -/// emph(it) -/// } else { -/// // Remaining cells aren't changed -/// it -/// } -/// } -/// -/// #grid( -/// columns: 3, -/// gutter: 3pt, -/// [Name], [Age], [Info], -/// [John], [52], [Nice], -/// [Mary], [50], [Cool], -/// [Jake], [49], [Epic] -/// ) -/// ``` +/// which allows you, for example, to apply styles based on a cell's position. +/// Refer to the examples of the [`table.cell`]($table.cell) element to learn +/// more about this. #[elem(name = "cell", title = "Grid Cell", Show)] pub struct GridCell { /// The cell's body. @@ -710,14 +744,21 @@ pub struct GridCell { /// position before cells with automatic positions). /// /// ```example + /// #let circ(c) = circle( + /// fill: c, width: 5mm + /// ) + /// /// #grid( /// columns: 4, - /// rows: 2.5em, - /// fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + /// rows: 7mm, + /// stroke: .5pt + blue, /// align: center + horizon, - /// inset: 3pt, - /// grid.cell(x: 2, y: 2)[3], - /// [1], grid.cell(x: 3)[4], [2], + /// inset: 1mm, + /// + /// grid.cell(x: 2, y: 2, circ(aqua)), + /// circ(yellow), + /// grid.cell(x: 3, circ(green)), + /// circ(black), /// ) /// ``` pub x: Smart, @@ -732,11 +773,21 @@ pub struct GridCell { /// columns in the chosen row are already occupied, an error is raised. /// /// ```example + /// #let tri(c) = polygon.regular( + /// fill: c, + /// size: 5mm, + /// vertices: 3, + /// ) + /// /// #grid( /// columns: 2, - /// fill: (x, y) => if calc.odd(x + y) { gray.lighten(40%) }, - /// inset: 1pt, - /// [A], grid.cell(y: 1)[B], grid.cell(y: 1)[C], grid.cell(y: 2)[D] + /// stroke: blue, + /// inset: 1mm, + /// + /// tri(black), + /// grid.cell(y: 1, tri(teal)), + /// grid.cell(y: 1, tri(red)), + /// grid.cell(y: 2, tri(orange)) /// ) /// ``` pub y: Smart, @@ -749,16 +800,16 @@ pub struct GridCell { #[default(NonZeroUsize::ONE)] pub rowspan: NonZeroUsize, - /// The cell's fill override. + /// The cell's [fill]($grid.fill) override. pub fill: Smart>, - /// The cell's alignment override. + /// The cell's [alignment]($grid.align) override. pub align: Smart, - /// The cell's inset override. + /// The cell's [inset]($grid.inset) override. pub inset: Smart>>>, - /// The cell's stroke override. + /// The cell's [stroke]($grid.stroke) override. #[resolve] #[fold] pub stroke: Sides>>>, diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs index c3bf3dbc4..daa057234 100644 --- a/crates/typst/src/model/table.rs +++ b/crates/typst/src/model/table.rs @@ -25,14 +25,26 @@ use crate::visualize::{Paint, Stroke}; /// Tables are used to arrange content in cells. Cells can contain arbitrary /// content, including multiple paragraphs and are specified in row-major order. /// Because tables are just grids with different defaults for some cell -/// properties (notably `stroke` and `inset`), refer to the -/// [grid documentation]($grid) for more information on how to size the table -/// tracks and specify the cell appearance properties. +/// properties (notably `stroke` and `inset`), refer to the [grid +/// documentation]($grid) for more information on how to size the table tracks +/// and specify the cell appearance properties. /// -/// Note that, to override a particular cell's properties or apply show rules -/// on table cells, you can use the [`table.cell`]($table.cell) element (but -/// not `grid.cell`, which is exclusive to grids). See its documentation for -/// more information. +/// If you are unsure whether you should be using a table or a grid, consider +/// whether the content you are arranging semantically belongs together as a set +/// of related data points or similar or whether you are just want to enhance +/// your presentation by arranging unrelated content in a grid. In the former +/// case, a table is the right choice, while in the latter case, a grid is more +/// appropriate. Furthermore, Typst will annotate its output in the future such +/// that screenreaders will annouce content in `table` as tabular while a grid's +/// content will be announced no different than multiple content blocks in the +/// document flow. +/// +/// Note that, to override a particular cell's properties or apply show rules on +/// table cells, you can use the [`table.cell`]($table.cell) element. See its +/// documentation for more information. +/// +/// Although the `table` and the `grid` share most properties, set and show +/// rules on one of them do not affect the other. /// /// To give a table a caption and make it [referenceable]($ref), put it into a /// [figure]. @@ -45,7 +57,9 @@ use crate::visualize::{Paint, Stroke}; /// columns: (1fr, auto, auto), /// inset: 10pt, /// align: horizon, -/// [], [*Area*], [*Parameters*], +/// table.header( +/// [], [*Area*], [*Parameters*], +/// ), /// image("cylinder.svg"), /// $ pi h (D^2 - d^2) / 4 $, /// [ @@ -63,33 +77,44 @@ use crate::visualize::{Paint, Stroke}; /// the appearance and the position of each cell. /// /// ```example -/// #set page(width: auto) +/// >>> #set page(width: auto) +/// >>> #set text(font: "IBM Plex Sans") +/// >>> #let gray = rgb("#565565") +/// >>> +/// #set table( +/// stroke: none, +/// gutter: 0.2em, +/// fill: (x, y) => +/// if x == 0 or y == 0 { gray }, +/// inset: (right: 1.5em), +/// ) +/// /// #show table.cell: it => { /// if it.x == 0 or it.y == 0 { /// set text(white) /// strong(it) /// } else if it.body == [] { /// // Replace empty cells with 'N/A' -/// pad(rest: it.inset)[_N/A_] +/// pad(..it.inset)[_N/A_] /// } else { /// it /// } /// } /// +/// #let a = table.cell( +/// fill: green.lighten(60%), +/// )[A] +/// #let b = table.cell( +/// fill: aqua.lighten(60%), +/// )[B] +/// /// #table( -/// fill: (x, y) => if x == 0 or y == 0 { gray.darken(50%) }, /// columns: 4, /// [], [Exam 1], [Exam 2], [Exam 3], -/// ..([John], [Mary], [Jake], [Robert]).map(table.cell.with(x: 0)), /// -/// // Mary got grade A on Exam 3. -/// table.cell(x: 3, y: 2, fill: green)[A], -/// -/// // Everyone got grade A on Exam 2. -/// ..(table.cell(x: 2, fill: green)[A],) * 4, -/// -/// // Robert got grade B on other exams. -/// ..(table.cell(y: 4, fill: aqua)[B],) * 2, +/// [John], [], a, [], +/// [Mary], [], a, a, +/// [Robert], b, a, b, /// ) /// ``` #[elem(scope, LayoutMultiple, LocalName, Figurable)] @@ -157,7 +182,7 @@ pub struct TableElem { /// ```example /// #table( /// columns: 3, - /// align: (x, y) => (left, center, right).at(x), + /// align: (left, center, right), /// [Hello], [Hello], [Hello], /// [A], [B], [C], /// ) @@ -172,10 +197,11 @@ pub struct TableElem { /// If it is necessary to place lines which can cross spacing between cells /// produced by the `gutter` option, or to override the stroke between /// multiple specific cells, consider specifying one or more of - /// [`table.hline`]($table.hline) and [`table.vline`]($table.vline) alongside - /// your table cells. + /// [`table.hline`]($table.hline) and [`table.vline`]($table.vline) + /// alongside your table cells. /// - /// See the [grid documentation]($grid) for more information on stroke. + /// See the [grid documentation]($grid.stroke) for more information on + /// strokes. #[resolve] #[fold] #[default(Celled::Value(Sides::splat(Some(Some(Arc::new(Stroke::default()))))))] @@ -464,6 +490,49 @@ impl TryFrom for TableItem { } /// A repeatable table header. +/// +/// You should wrap your tables' heading rows in this function even if you do not +/// plan to wrap your table across pages because Typst will use this function to +/// attach accessibility metadata to tables in the future and ensure universal +/// access to your document. +/// +/// You can use the `repeat` parameter to control whether your table's header +/// will be repeated across pages. +/// +/// ```example +/// #set page(height: 11.5em) +/// #set table( +/// fill: (x, y) => +/// if x == 0 or y == 0 { +/// gray.lighten(40%) +/// }, +/// align: right, +/// ) +/// +/// #show table.cell.where(x: 0): strong +/// #show table.cell.where(y: 0): strong +/// +/// #table( +/// columns: 4, +/// table.header( +/// [], [Blue chip], +/// [Fresh IPO], [Penny st'k], +/// ), +/// table.cell( +/// rowspan: 6, +/// align: horizon, +/// rotate(-90deg, reflow: true)[ +/// *USD / day* +/// ], +/// ), +/// [0.20], [104], [5], +/// [3.17], [108], [4], +/// [1.59], [84], [1], +/// [0.26], [98], [15], +/// [0.01], [195], [4], +/// [7.34], [57], [2], +/// ) +/// ``` #[elem(name = "header", title = "Table Header")] pub struct TableHeader { /// Whether this header should be repeated across pages. @@ -476,6 +545,13 @@ pub struct TableHeader { } /// A repeatable table footer. +/// +/// Just like the [`table.header`]($table.header) element, the footer can repeat +/// itself on every page of the table. This is useful for improving legibility +/// by adding the column labels in both the header and footer of a large table, +/// totals, or other information that should be visible on every page. +/// +/// No other table cells may be placed after the footer. #[elem(name = "footer", title = "Table Footer")] pub struct TableFooter { /// Whether this footer should be repeated across pages. @@ -487,17 +563,42 @@ pub struct TableFooter { pub children: Vec, } -/// A horizontal line in the table. See the docs for -/// [`grid.hline`]($grid.hline) for more information regarding how to use this -/// element's fields. +/// A horizontal line in the table. /// /// Overrides any per-cell stroke, including stroke specified through the -/// table's `stroke` field. Can cross spacing between cells created through -/// the table's `column-gutter` option. +/// table's `stroke` field. Can cross spacing between cells created through the +/// table's [`column-gutter`]($table.column-gutter) option. +/// +/// Use this function instead of the table's `stroke` field if you want to +/// manually place a horizontal line at a specific position in a single table. +/// Consider using [table's `stroke`]($table.stroke) field or [`table.cell`'s +/// `stroke`]($table.cell.stroke) field instead if the line you want to place is +/// part of all your tables' designs. +/// +/// ```example +/// #set table.hline(stroke: .6pt) +/// +/// #table( +/// stroke: none, +/// columns: (auto, 1fr), +/// [09:00], [Badge pick up], +/// [09:45], [Opening Keynote], +/// [10:30], [Talk: Typst's Future], +/// [11:15], [Session: Good PRs], +/// table.hline(start: 1), +/// [Noon], [_Lunch break_], +/// table.hline(start: 1), +/// [14:00], [Talk: Tracked Layout], +/// [15:00], [Talk: Automations], +/// [16:00], [Workshop: Tables], +/// table.hline(), +/// [19:00], [Day 1 Attendee Mixer], +/// ) +/// ``` #[elem(name = "hline", title = "Table Horizontal Line")] pub struct TableHLine { /// The row above which the horizontal line is placed (zero-indexed). - /// Functions identically to the `y` field in [`grid.hline`]($grid.hline). + /// Functions identically to the `y` field in [`grid.hline`]($grid.hline.y). pub y: Smart, /// The column at which the horizontal line starts (zero-indexed, inclusive). @@ -531,8 +632,14 @@ pub struct TableHLine { /// for more information regarding how to use this element's fields. /// /// Overrides any per-cell stroke, including stroke specified through the -/// table's `stroke` field. Can cross spacing between cells created through -/// the table's `row-gutter` option. +/// table's `stroke` field. Can cross spacing between cells created through the +/// table's [`row-gutter`]($table.row-gutter) option. +/// +/// Similar to [`table.hline`]($table.hline), use this function if you want to +/// manually place a vertical line at a specific position in a single table and +/// use the [table's `stroke`]($table.stroke) field or [`table.cell`'s +/// `stroke`]($table.cell.stroke) field instead if the line you want to place is +/// part of all your tables' designs. #[elem(name = "vline", title = "Table Vertical Line")] pub struct TableVLine { /// The column before which the horizontal line is placed (zero-indexed). @@ -571,50 +678,85 @@ pub struct TableVLine { pub position: OuterHAlignment, } -/// A cell in the table. Use this to either override table properties for a -/// particular cell, or in show rules to apply certain styles to multiple cells -/// at once. +/// A cell in the table. Use this to position a cell manually or to apply +/// styling. To do the latter, you can either use the function to override the +/// properties for a particular cell, or use it in show rules to apply certain +/// styles to multiple cells at once. +/// +/// Perhaps the most important use-case of `{table.cell}` is to make a cell span +/// multiple columns or rows with the `colspan` and `rowspan` fields. +/// +/// ```example +/// >>> #set page(width: auto) +/// >>> #show table.cell.where(y: 0): strong +/// >>> #set table( +/// >>> stroke: (x, y) => if y == 0 { +/// >>> (bottom: 0.7pt + black) +/// >>> }, +/// >>> align: (x, y) => +/// >>> if x > 0 { center } +/// >>> else { left } +/// >>> ) +/// #table( +/// columns: 3, +/// table.header( +/// [Substance], +/// [Subcritical °C], +/// [Supercritical °C], +/// ), +/// [Hydrochloric Acid], +/// [12.0], [92.1], +/// [Sodium Myreth Sulfate], +/// [16.6], [104], +/// [Potassium Hydroxide], +/// table.cell(colspan: 2)[24.7], +/// ) +/// ``` /// /// For example, you can override the fill, alignment or inset for a single /// cell: /// /// ```example +/// >>> #set page(width: auto) +/// // You can also import those. +/// #import table: cell, header +/// /// #table( /// columns: 2, -/// fill: green, -/// align: right, -/// [*Name*], [*Data*], -/// table.cell(fill: blue)[J.], [Organizer], -/// table.cell(align: center)[K.], [Leader], -/// [M.], table.cell(inset: 0pt)[Player] +/// align: center, +/// header( +/// [*Trip progress*], +/// [*Itinerary*], +/// ), +/// cell( +/// align: right, +/// fill: fuchsia.lighten(80%), +/// [🚗], +/// ), +/// [Get in, folks!], +/// [🚗], [Eat curbside hotdog], +/// cell(align: left)[🌴🚗], +/// cell( +/// inset: 0.06em, +/// text(1.62em)[🛖🌅🌊], +/// ), /// ) /// ``` /// -/// You may also apply a show rule on `table.cell` to style all cells at once, -/// which allows you, for example, to apply styles based on a cell's position: +/// You may also apply a show rule on `table.cell` to style all cells at once. +/// Combined with selectors, this allows you to apply styles based on a cell's +/// position: /// /// ```example -/// #show table.cell: it => { -/// if it.y == 0 { -/// // First row is bold -/// strong(it) -/// } else if it.x == 1 { -/// // Second column is italicized -/// // (except at the first row) -/// emph(it) -/// } else { -/// // Remaining cells aren't changed -/// it -/// } -/// } +/// #show table.cell.where(x: 0): strong /// /// #table( /// columns: 3, /// gutter: 3pt, -/// [Name], [Age], [Info], -/// [John], [52], [Nice], -/// [Mary], [50], [Cool], -/// [Jake], [49], [Epic] +/// [Name], [Age], [Strength], +/// [Hannes], [36], [Grace], +/// [Irma], [50], [Resourcefulness], +/// [Vikram], [49], [Perseverance], /// ) /// ``` #[elem(name = "cell", title = "Table Cell", Show)] @@ -631,9 +773,6 @@ pub struct TableCell { /// Functions identically to the `y` field in [`grid.cell`]($grid.cell). pub y: Smart, - /// The cell's fill override. - pub fill: Smart>, - /// The amount of columns spanned by this cell. #[default(NonZeroUsize::ONE)] pub colspan: NonZeroUsize, @@ -642,13 +781,16 @@ pub struct TableCell { #[default(NonZeroUsize::ONE)] rowspan: NonZeroUsize, - /// The cell's alignment override. + /// The cell's [fill]($table.fill) override. + pub fill: Smart>, + + /// The cell's [alignment]($table.align) override. pub align: Smart, - /// The cell's inset override. + /// The cell's [inset]($table.inset) override. pub inset: Smart>>>, - /// The cell's stroke override. + /// The cell's [stroke]($table.stroke) override. #[resolve] #[fold] pub stroke: Sides>>>,