Search code examples
typst

How to send content into specific boxes in typst?


In typst I can use column to split content/text into columns on the page, is there a way for me to control the spreading/splitting of content into different boxes in a more granular fashion?

For example, say I have a rudimentary dictionary that looks like this


Items:
-
  Section: Foo
  Details:
    ABCD: EFGH
    HIJK: KLMN
-
  Section: Bar
  Details:
    OPQR: STUV
    WXYZ: 1234
- 
  Section: Tres
  Details:
      Random: Thought
      Test: Value


And I am calling it using code that looks like this:-


#let content = yaml("dictionary.yaml") // Get the content file

#let test_item(test_item) = {
  [
    *#underline[#test_item.Section]* \
    #for (hit, details) in test_item.Details [
      *#hit* : #details \
    ] #v(-0.25em) // Add some space for the next section
  ]
}

#box(height: 75pt,
 columns(2, gutter: 11pt)[
    #set par(justify: true)
    #for entry in content.Items {
      test_item(entry)
    }
 ]
)


Is there a way I can direct content to specific boxes in the column. What function/utility of script should I be reading into?

Right now the compiler decides on its own - but is there a way I can control it so I can tell the compiler to send a item to a very specific column.

This does not have to be done with `columns` if there is some other way to script what specific section of the page content ends up in.

Updating the question

My goal was to create a dictionary with the following structure:

information.yaml

Items:
-
  Section: Foo
  Details:
    ABCD: EFGH
    HIJK: KLMN
-
  Section: Bar
  Details:
    OPQR: STUV
    WXYZ: 1234
-
  Section: Tres
  Details:
    Random: Thought
    Test: Value

I hoped to generate a document that resembles this:- enter image description here

(Note: I created this manually, but it's not scalable or easy to automate.)

Following up on the suggestion that grid might be better suited for this than column I tried the following code:

#let content = yaml("information.yaml") // Load the content file

#let test_item(test_item) = {
  [
    *#underline[#test_item.Section]* \\
    #for (hit, details) in test_item.Details [
      *#hit* : #details \\
    ] #v(-0.25em) // Add space before the next section
  ]
}

#grid(
  columns: 2,
  column-gutter: 1em,
  let column_value = 1,
  for entry in content.Items {
    grid.cell(
      x: column_value,
      skills_entry(entry)
    )
    grid.cell(x: column_value, test_item(entry))
    if (column_value == 1) {
      column_value = 0
    } else if (column_value == 0) {
      column_value = 1
    }
  }
)

My intention with the if and else if syntax was to alternate between the first and second columns, cycling between 0 and 1. However, this approach didn't work, and all dictionary items ended up in the first column.

The code produced this document:-

enter image description here

As I'm only somewhat familiar with variable scoping, I tried using printf-style debugging to see if column_value was being updated:

#let tri(c, val) = {
  polygon.regular(
    fill: c,
    size: 5mm,
    vertices: 3,
  )
  [The value is #val]
}

#grid(
  columns: 2,
  stroke: blue,
  inset: 1mm,
  let col_val = 1,
  for i in range(4) {
    grid.cell(x: col_val, tri(green, col_val))
    if (col_val == 1) {
      col_val = 0
    } else if (col_val == 0) {
      col_val = 1
    }
  }
)

This produced

enter image description here

The outcome has me completely confused: the value of col_val is updated, but it doesn't affect the actual grid.cell - is this where I should start reading into state.

I suspect I might be going about this the wrong way and there is probably a more elegant and less hacky solution - if so could someone help me out?


Solution

  • Turns out I can just do this

    #let content = yaml("information.yaml") // Load the content file
    
    #let test_item(test_item) = [
      *#underline[#test_item.Section]* \
      #for (hit, details) in test_item.Details [
        *#hit*: #details \
      ]
      #v(-0.25em) // Add space before the next section
    ]
    
    #grid(
      columns: (1fr, 1fr),
      column-gutter: 1em,
      row-gutter: 1em,
      ..content.Items.map(entry => test_item(entry))
    )
    

    The crucial part was ..{<dictionary>}.map(item => function)

    Which makes

    enter image description here

    i.e. automatically distributing the items across the columns one at a time.