203 lines
4.7 KiB
Typst
203 lines
4.7 KiB
Typst
#let path = sys.inputs.at("path", default: "menu.json")
|
|
#let all-combos = sys.inputs.at("all-combos", default: "false") == "true"
|
|
#let categories = sys.inputs.at("categories", default: "E,D,C,V").split(",")
|
|
#let dark = sys.inputs.at("dark", default: "false") == "true"
|
|
#let days = json(path)
|
|
#let nbsp = sym.space.nobreak
|
|
|
|
#let colors = if dark {(
|
|
bg: rgb("#1f1f24"),
|
|
text: white,
|
|
categories: rgb("#70c947"),
|
|
menu-sep: rgb("#ababab"),
|
|
title-underline: rgb("#eb2324"),
|
|
day-sep: rgb("#a3cf40")
|
|
)} else {(
|
|
bg: white,
|
|
text: black,
|
|
categories: rgb("#577F25"),
|
|
menu-sep: rgb("#ababab"),
|
|
title-underline: rgb("#e34243"),
|
|
day-sep: rgb("#82BC00")
|
|
)}
|
|
|
|
#let width = if days.len() > 1 {20cm} else {10cm}
|
|
|
|
#set page(
|
|
margin: 0cm,
|
|
height: auto,
|
|
width: width,
|
|
fill: colors.bg
|
|
)
|
|
|
|
#set text(
|
|
fill: colors.text
|
|
)
|
|
|
|
#let parse-date(date-txt) = {
|
|
let (year, month, day) = date-txt.split("-")
|
|
return datetime(year: int(year), month: int(month), day: int(day))
|
|
}
|
|
|
|
#let fmt-number(
|
|
number,
|
|
decimals: 2,
|
|
decimal-sep: ".",
|
|
thousands-sep: "'"
|
|
) = {
|
|
let integer = calc.trunc(number)
|
|
let decimal = calc.fract(number)
|
|
let res = str(integer).clusters()
|
|
.rev()
|
|
.chunks(3)
|
|
.map(c => c.join(""))
|
|
.join(thousands-sep)
|
|
.rev()
|
|
|
|
if decimals != 0 {
|
|
res += decimal-sep
|
|
decimal = decimal * calc.pow(10, decimals)
|
|
decimal = calc.round(decimal)
|
|
decimal = str(decimal) + ("0" * decimals)
|
|
res += decimal.slice(0, decimals)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
#let day-names = (
|
|
"Lundi",
|
|
"Mardi",
|
|
"Mercredi",
|
|
"Jeudi",
|
|
"Vendredi",
|
|
"Samedi",
|
|
"Dimanche"
|
|
)
|
|
|
|
#let display-menus(
|
|
days,
|
|
categories,
|
|
day-sep-stroke: colors.day-sep + 1pt,
|
|
title-underline-stroke: colors.title-underline + 2pt,
|
|
menu-sep-stroke: colors.menu-sep + 1pt,
|
|
price-categ-color: colors.categories
|
|
) = {
|
|
set text(font: "Liberation Sans", hyphenate: true)
|
|
let cols = calc.min(2, days.len())
|
|
|
|
let cells = ()
|
|
|
|
for day in days {
|
|
let date = parse-date(day.date)
|
|
let weekday = date.weekday() - 1
|
|
let day-name = day-names.at(weekday)
|
|
let menus = ()
|
|
for menu in day.menus {
|
|
let price-cell = grid.cell(rowspan: 2)[]
|
|
let prices = menu.at(
|
|
"prices",
|
|
default: (:)
|
|
)
|
|
if menu.keys().contains("price") {
|
|
prices = ("": menu.price)
|
|
}
|
|
|
|
let pairs = prices.pairs()
|
|
.filter(p => p.first() in categories)
|
|
.sorted(key: p => p.last())
|
|
|
|
let prices = pairs.map(p => {
|
|
let categ = emph(
|
|
text(
|
|
fill: price-categ-color,
|
|
p.first()
|
|
)
|
|
)
|
|
let price = fmt-number(p.last())
|
|
categ + ":" + nbsp + "CHF" + nbsp + price
|
|
})
|
|
price-cell = grid.cell(
|
|
rowspan: menu.extra.len() + 1,
|
|
stack(dir: ttb, spacing: .4em, ..prices)
|
|
)
|
|
//let price = "CHF" + sym.space.nobreak + str(menu.price)
|
|
menus.push(
|
|
grid(
|
|
columns: (1fr, auto),
|
|
column-gutter: .4em,
|
|
row-gutter: .4em,
|
|
[*#menu.name*],
|
|
price-cell,
|
|
..menu.extra.map(l => text(10pt, l))
|
|
)
|
|
)
|
|
}
|
|
|
|
let title = align(
|
|
center,
|
|
stack(
|
|
dir: ttb,
|
|
spacing: .5em,
|
|
text(size: 14pt, weight: "bold", day-name),
|
|
text(size: 10pt, date.display("[day].[month]"))
|
|
)
|
|
)
|
|
let title-box = box(
|
|
width: 50%,
|
|
inset: .6em,
|
|
stroke: (bottom: title-underline-stroke),
|
|
title
|
|
)
|
|
let title-cell = grid.cell(align: center, title-box)
|
|
|
|
cells.push(box(width: 100%, grid(
|
|
columns: (100%),
|
|
inset: (x, y) => (
|
|
top: if y == 0 {0pt} else {.8em},
|
|
bottom: if y == 0 {0pt} else {2em}
|
|
),
|
|
stroke: (_, y) => (top: if y < 2 {none} else {menu-sep-stroke}),
|
|
title-cell,
|
|
..menus
|
|
)))
|
|
}
|
|
|
|
if calc.rem(cells.len(), 2) == 1 {
|
|
cells.last() = grid.cell(colspan: calc.min(2, cols), cells.last())
|
|
}
|
|
|
|
grid(
|
|
columns: (100% / cols,) * cols,
|
|
inset: (x: 1.5em, y: .8em),
|
|
stroke: (x, y) => {
|
|
let borders = (:)
|
|
if x != 0 {
|
|
borders.insert("left", day-sep-stroke)
|
|
}
|
|
if y != 0 {
|
|
borders.insert("top", day-sep-stroke)
|
|
}
|
|
return borders
|
|
},
|
|
..cells
|
|
)
|
|
}
|
|
|
|
#{
|
|
if all-combos {
|
|
let n-combos = calc.pow(2, categories.len())
|
|
for i in range(n-combos) {
|
|
let filtered-categories = categories.enumerate().filter(p => {
|
|
let (j, categ) = p
|
|
return i.bit-rshift(j).bit-and(1) != 0
|
|
}).map(p => p.last())
|
|
display-menus(days, filtered-categories)
|
|
pagebreak(weak: true)
|
|
}
|
|
|
|
} else {
|
|
display-menus(days, categories)
|
|
}
|
|
}
|