diff options
author | Jean-Nicolas Veigel <art.jnveigel@gmail.com> | 2024-03-16 05:30:44 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2024-03-26 10:31:25 +0000 |
commit | e3a94ea6f66f4797d2d179edbf820c9efd943a03 (patch) | |
tree | 874e1b1e4a246ba07bd4437fec1da8d0ca34e1bf /src | |
parent | ca2ec291c7ca1972aae39cfe1bb48332d8030ff7 (diff) | |
download | tour-e3a94ea6f66f4797d2d179edbf820c9efd943a03.tar.gz tour-e3a94ea6f66f4797d2d179edbf820c9efd943a03.zip |
feat: move everything page generation template to own module
Diffstat (limited to 'src')
-rw-r--r-- | src/tour.gleam | 167 | ||||
-rw-r--r-- | src/tour/pages/everything.gleam | 151 | ||||
-rw-r--r-- | src/tour/types/lesson.gleam | 16 |
3 files changed, 171 insertions, 163 deletions
diff --git a/src/tour.gleam b/src/tour.gleam index 4834c9b..c24111a 100644 --- a/src/tour.gleam +++ b/src/tour.gleam @@ -1,16 +1,18 @@ import filepath import gleam/io import gleam/list -import gleam/option.{type Option, None, Some} +import gleam/option.{None, Some} import gleam/result import gleam/string import gleam/string_builder import htmb.{h, text} import simplifile import snag +import tour/types/lesson.{type Chapter, type Lesson, Chapter, Lesson} import tour/document import tour/page.{PageConfig, ScriptConfig} import tour/styles +import tour/pages/everything const static = "static" @@ -130,21 +132,6 @@ pub fn main() { } } -type Chapter { - Chapter(name: String, path: String, lessons: List(Lesson)) -} - -type Lesson { - Lesson( - name: String, - text: String, - code: String, - path: String, - previous: Option(String), - next: Option(String), - ) -} - type FileNames { FileNames(path: String, name: String, slug: String) } @@ -293,7 +280,7 @@ fn write_everything_page(chapters: List(Chapter)) -> snag.Result(Nil) { let path = public <> "/everything" use _ <- result.try(ensure_directory(path)) let path = filepath.join(path, "/index.html") - write_text(path, everything_html(chapters)) + write_text(path, everything.render(chapters)) } fn write_lesson(lesson: Lesson) -> snag.Result(Nil) { @@ -517,142 +504,6 @@ fn file_error( } } -fn slugify_path(path: String) -> String { - string.replace(path, "/", "-") - |> string.drop_left(up_to: 1) -} - -fn separator(class: String) { - h("hr", [#("class", class <> "-separator")], []) -} - -fn everything_chapter_lesson_html(lesson: Lesson, index: Int, end_index: Int) { - let snippet_link_title = "Experiment with " <> lesson.name <> " in browser" - - let lesson_content = - h("article", [#("class", "lesson"), #("id", slugify_path(lesson.path))], [ - h("a", [#("href", "#" <> slugify_path(lesson.path)), #("class", "link")], [ - h("h2", [#("class", "lesson-title")], [text(lesson.name)]), - ]), - htmb.dangerous_unescaped_fragment(string_builder.from_string(lesson.text)), - h("pre", [#("class", "lesson-snippet hljs gleam language-gleam")], [ - h("code", [], [text(lesson.code)]), - h( - "a", - [ - #("class", "lesson-snippet-link"), - #("href", lesson.path), - #("title", snippet_link_title), - #("aria-label", snippet_link_title), - ], - [ - h("i", [#("class", "snippet-link-icon")], [text("</>")]), - text("Run code snippet"), - ], - ), - ]), - ]) - - case index { - i if i == end_index -> [lesson_content] - _ -> [lesson_content, separator("lesson")] - } -} - -fn everything_html(chapters: List(Chapter)) -> String { - let chapter_lessons = { - use #(chapter, index) <- list.flat_map( - list.index_map(chapters, fn(chap, i) { #(chap, i) }), - ) - - let end_lesson_index = list.length(chapter.lessons) - 1 - - let chapter_lessons = - chapter.lessons - |> list.index_map(fn(lesson, index) { - everything_chapter_lesson_html(lesson, index, end_lesson_index) - }) - - let chapter_title = - h( - "h3", - [#("id", slugify_path(chapter.path)), #("class", "chapter-title")], - [text(chapter.name)], - ) - - let chapter_header = case index { - 0 -> [chapter_title, separator("chapter")] - _ -> [separator("chapter-between"), chapter_title, separator("chapter")] - } - - list.concat([chapter_header, ..chapter_lessons]) - } - - let table_of_contents = - list.map(chapters, fn(chapter) { - h( - "article", - [#("class", "chapter"), #("id", "chapter-" <> chapter.name)], - [ - h("h3", [], [text(chapter.name)]), - h( - "ul", - [], - list.map(chapter.lessons, fn(lesson) { - h("li", [], [ - link_html( - Link(label: lesson.name, to: "#" <> slugify_path(lesson.path)), - [#("class", "link padded")], - ), - ]) - }), - ), - ], - ) - }) - - // TODO: use proper values for location and such - page.html( - page.PageConfig( - path: "everything", - title: "Everything!", - scripts: page.ScriptConfig( - head: [ - document.script( - "/js/highlight/highlight.core.min.js", - document.ScriptOptions(module: True, defer: False), - [], - ), - document.script( - "/js/highlight/regexes.js", - document.ScriptOptions(module: True, defer: True), - [], - ), - ], - body: [ - document.script( - "/js/highlight/highlight-gleam.js", - document.ScriptOptions(module: True, defer: True), - [], - ), - ], - ), - stylesheets: [styles.page, ..styles.defaults_code], - content: [ - h("main", [#("id", "everything")], [ - h( - "aside", - [#("id", "everything-contents"), #("class", "dim-bg")], - table_of_contents, - ), - h("section", [#("id", "everything-lessons")], chapter_lessons), - ]), - ], - ), - ) - |> page.render -} - fn lesson_html(lesson: Lesson) -> String { let navlink = fn(name, link) { case link { @@ -708,13 +559,3 @@ fn lesson_html(lesson: Lesson) -> String { ) |> page.render } - -type Link { - Link(label: String, to: String) -} - -fn link_html(for link: Link, attributes attributes: List(#(String, String))) { - let link_attributes = [#("href", link.to), ..attributes] - - h("a", link_attributes, [text(link.label)]) -} diff --git a/src/tour/pages/everything.gleam b/src/tour/pages/everything.gleam new file mode 100644 index 0000000..04f0651 --- /dev/null +++ b/src/tour/pages/everything.gleam @@ -0,0 +1,151 @@ +//// tour.gleam.run/everyting +//// +//// Displays a list of all chapters and their lessons +//// as well as a table of contents that allows for easy navigation + +import gleam/list +import gleam/string +import gleam/string_builder +import htmb.{type Html, dangerous_unescaped_fragment, h, text} +import tour/types/lesson.{type Chapter, type Lesson} +import tour/components.{Link} +import tour/document +import tour/page.{PageConfig, ScriptConfig} +import tour/styles + +const page_css = "/css/pages/everything.css" + +fn slugify_path(path: String) -> String { + string.replace(path, "/", "-") + |> string.drop_left(up_to: 1) +} + +fn separator(class: String) { + h("hr", [#("class", class <> "-separator")], []) +} + +fn lesson_html(lesson: Lesson, index: Int, end_index: Int) { + let snippet_link_title = "Experiment with " <> lesson.name <> " in browser" + + let lesson_content = + h("article", [#("class", "lesson"), #("id", slugify_path(lesson.path))], [ + h("a", [#("href", "#" <> slugify_path(lesson.path)), #("class", "link")], [ + h("h2", [#("class", "lesson-title")], [text(lesson.name)]), + ]), + dangerous_unescaped_fragment(string_builder.from_string(lesson.text)), + h("pre", [#("class", "lesson-snippet hljs gleam language-gleam")], [ + h("code", [], [text(lesson.code)]), + h( + "a", + [ + #("class", "lesson-snippet-link"), + #("href", lesson.path), + #("title", snippet_link_title), + #("aria-label", snippet_link_title), + ], + [ + h("i", [#("class", "snippet-link-icon")], [text("</>")]), + text("Run code snippet"), + ], + ), + ]), + ]) + + case index { + i if i == end_index -> [lesson_content] + _ -> [lesson_content, separator("lesson")] + } +} + +fn chapters_html(chapters: List(Chapter)) -> List(Html) { + use #(chapter, index) <- list.flat_map( + list.index_map(chapters, fn(chap, i) { #(chap, i) }), + ) + + let lessons = + list.index_map(chapter.lessons, fn(lesson, index) { + lesson_html(lesson, index, list.length(chapter.lessons) - 1) + }) + let chapter_title = + h("h3", [#("id", slugify_path(chapter.path)), #("class", "chapter-title")], [ + text(chapter.name), + ]) + + let chapter_header = case index { + 0 -> [chapter_title, separator("chapter")] + _ -> [separator("chapter-between"), chapter_title, separator("chapter")] + } + + list.concat([chapter_header, ..lessons]) +} + +fn toc_link(lesson: Lesson) -> Html { + h("li", [], [ + components.text_link( + Link(label: lesson.name, to: "#" <> slugify_path(lesson.path)), + [#("class", "link padded")], + ), + ]) +} + +fn toc_html(chapters: List(Chapter)) -> List(Html) { + use chapter <- list.map(chapters) + let links = list.map(chapter.lessons, toc_link) + + h("article", [#("class", "chapter"), #("id", "chapter-" <> chapter.name)], [ + h("h3", [], [text(chapter.name)]), + h("ul", [], links), + ]) +} + +/// Builds the /everything's page body content +fn html(chapters: List(Chapter)) -> Html { + let chapter_lessons = chapters_html(chapters) + let table_of_contents = toc_html(chapters) + + h("main", [#("id", "everything")], [ + h( + "aside", + [#("id", "everything-contents"), #("class", "dim-bg")], + table_of_contents, + ), + h("section", [#("id", "everything-lessons")], chapter_lessons), + ]) +} + +/// Renders the /everything page to a string +pub fn render(chapters: List(Chapter)) -> String { + let content = html(chapters) + + // TODO: use proper values for location and such + page.html( + PageConfig( + path: "everything", + title: "Everything!", + scripts: ScriptConfig( + head: [ + document.script( + "/js/highlight/highlight.core.min.js", + document.ScriptOptions(module: True, defer: False), + [], + ), + document.script( + "/js/highlight/regexes.js", + document.ScriptOptions(module: True, defer: True), + [], + ), + ], + body: [ + document.script( + "/js/highlight/highlight-gleam.js", + document.ScriptOptions(module: True, defer: True), + [], + ), + ], + ), + stylesheets: [styles.page, page_css, ..styles.defaults_code], + content: [content], + ), + ) + |> page.render +} diff --git a/src/tour/types/lesson.gleam b/src/tour/types/lesson.gleam new file mode 100644 index 0000000..10fbdd9 --- /dev/null +++ b/src/tour/types/lesson.gleam @@ -0,0 +1,16 @@ +import gleam/option.{type Option} + +pub type Chapter { + Chapter(name: String, path: String, lessons: List(Lesson)) +} + +pub type Lesson { + Lesson( + name: String, + text: String, + code: String, + path: String, + previous: Option(String), + next: Option(String), + ) +} |