typstuff/misc/menus/menu.typ

203 lines
4.7 KiB
Typst
Raw Normal View History

2024-12-13 23:04:02 +01:00
#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)
}
}