Rust library to generate static websites
1use maud::{Markup, html};
2use maudit::{
3 content::MarkdownHeading,
4 route::{PageContext, RouteExt},
5};
6
7use crate::{
8 content::{DocsContent, DocsSection},
9 routes::{DocsPage, DocsPageParams},
10};
11
12pub fn left_sidebar(ctx: &mut PageContext) -> Markup {
13 let content = ctx.content.get_source::<DocsContent>("docs");
14
15 let mut sections = std::collections::HashMap::new();
16
17 for entry in content.entries.iter() {
18 if let Some(section) = &entry.data(ctx).section {
19 sections.entry(section).or_insert_with(Vec::new).push(entry);
20 }
21 }
22
23 let mut sections: Vec<_> = sections.into_iter().collect();
24
25 // TODO: Implement sorting on the enum ord itself?
26 sections.sort_by_key(|(section, _)| {
27 // Define sort order
28 match section {
29 DocsSection::GettingStarted => 0,
30 DocsSection::CoreConcepts => 1,
31 DocsSection::Guide => 2,
32 DocsSection::Advanced => 3,
33 }
34 });
35
36 let static_links: Vec<(&str, &str)> = vec![
37 ("Reference", "https://docs.rs/maudit"),
38 (
39 "Examples",
40 "https://github.com/bruits/maudit/tree/main/examples",
41 ),
42 ];
43
44 let entries = sections.iter().map(|(section, entries)| {
45 html! {
46 li.mb-6.sm:mb-4 {
47 h2.text-xl.sm:text-lg.font-bold { (section) }
48 ul {
49 @for entry in entries {
50 @let url = DocsPage.url(DocsPageParams { slug: entry.id.clone() });
51 @let is_current_page = url == *ctx.current_path;
52 li {
53 @let base_classes = "block py-2 sm:py-1 px-4 sm:px-3 text-lg sm:text-base font-medium sm:font-normal transition-colors border-b border-borders sm:border-0";
54 @let conditional_classes = if is_current_page {
55 "text-brand-red sm:border-l-2 sm:border-l-brand-red"
56 } else {
57 "text-our-black hover:text-brand-red sm:border-l-2 sm:border-l-borders sm:hover:border-l-brand-red"
58 };
59 a class=(format!("{} {}", base_classes, conditional_classes)) href=(url) {
60 (entry.data(ctx).title)
61 }
62 }
63 }
64 }
65 }
66 }
67 });
68
69 html! {
70 ul.mb-6.sm:mb-4.space-y-0.sm:space-y-1 {
71 @for (name, link) in static_links {
72 li {
73 a.block.py-2.sm:py-0.px-4.sm:px-0.text-xl.sm:text-lg.font-medium.text-our-black.border-b.border-borders.sm:border-0.transition-colors."hover:text-brand-red".sm:bg-transparent.sm:hover:bg-transparent href=(link) { (name) }
74 }
75 }
76 }
77 ul.space-y-1 {
78 @for entry in entries {
79 (entry)
80 }
81 }
82 }
83}
84
85pub fn right_sidebar(headings: &[MarkdownHeading]) -> Markup {
86 let mut html_headings: Vec<maud::PreEscaped<String>> = Vec::new();
87 let mut i = 0;
88 let mut seen_h2 = false;
89 while i < headings.len() {
90 let heading = &headings[i];
91 let (pad, border) = match heading.level {
92 2 => ("pl-0", ""), // h2
93 3 => ("pl-4", "sm:border-l-2 sm:border-borders"), // h3
94 4 => ("pl-8", "sm:border-l-2 sm:border-borders"), // h4
95 5 => ("pl-12", "sm:border-l-2 sm:border-borders"), // h5
96 6 => ("pl-16", "sm:border-l-2 sm:border-borders"), // h6
97 _ => ("pl-0", ""), // fallback
98 };
99 let next_level = if i + 1 < headings.len() {
100 headings[i + 1].level
101 } else {
102 0
103 };
104 let margin_top = if heading.level == 2 && next_level > 2 && seen_h2 {
105 "mt-4"
106 } else {
107 ""
108 };
109 if heading.level == 2 {
110 seen_h2 = true;
111 }
112 html_headings.push(html! {
113 li.(border).(margin_top) {
114 a class=(format!("block py-1 px-3 sm:py-0 text-lg sm:text-base transition-colors hover:bg-gray-50 sm:hover:bg-transparent hover:text-brand-red border-b border-borders sm:border-b-0 {}", pad)) href=(format!("#{}", heading.id)) {
115 (heading.title)
116 }
117 }
118 });
119 i += 1;
120 }
121
122 html!(
123 h2.text-xl.sm:text-lg.font-bold.mb-4.sm:mb-0 { "On This Page" }
124 nav.sticky.top-8 {
125 ul {
126 @for heading in html_headings {
127 (heading)
128 }
129 }
130 }
131 )
132}