feat: Adds a tag grid at top of articles view.
All checks were successful
CI / release (push) Successful in 6m34s

This commit is contained in:
2025-04-15 10:17:04 -04:00
parent 88c6bd4891
commit def75c1e41
4 changed files with 51 additions and 12 deletions

View File

@@ -2,12 +2,6 @@ import Foundation
import HTML
import Saga
func uniqueTagsWithCount(_ articles: [Item<ArticleMetadata>]) -> [(String, Int)] {
let tags = articles.flatMap { $0.metadata.tags }
let tagsWithCounts = tags.reduce(into: [:]) { $0[$1, default: 0] += 1 }
return tagsWithCounts.sorted { $0.1 > $1.1 }
}
func renderArticles(context: ItemsRenderingContext<ArticleMetadata>) -> Node {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy"
@@ -16,9 +10,9 @@ func renderArticles(context: ItemsRenderingContext<ArticleMetadata>) -> Node {
let sortedByYearDescending = articlesPerYear.sorted { $0.key > $1.key }
return baseLayout(canocicalURL: "/articles/", section: .articles, title: "Articles", rssLink: "", extraHeader: "") {
// TODO: Add list of tags here that can be navigated to.
sortedByYearDescending.map { year, articles in
div(class: "mt-8 bg-slate-800 rounded-lg") {
div(class: "mt-8 mb-10 bg-slate-800 border border-slate-200 rounded-lg") {
TagGrid(items: context.items)
div(class: "border-b border-light pt-6 w-full") {
div(class: "px-6 flex flex-row gap-4 ") {
img(src: "/static/img/calendar.svg", width: "40")

View File

@@ -0,0 +1,45 @@
import HTML
import Saga
struct TagGrid: NodeConvertible {
let items: [Item<ArticleMetadata>]
func asNode() -> Node {
div(class: "mt-1 bg-slate-950 rounded-ss-lg border-b border-slate-200") {
// Grid Header
div(class: "flex flex-row gap-4 px-4 pt-4") {
img(src: "/static/img/tag.svg", width: "40")
h1 { "Tags" }
}
div(class: "px-4 pb-8 -mt-2") {
span(class: "text-sm text-green-300") { "Click on a tag to view related articles." }
}
// Grid items.
div(class: "grid sm:grid-cols-2 lg:grid-cols-4 gap-4 px-6 pb-6") {
uniqueTagsWithCount(items).map { tag, count in
div {
a(class: "bg-slate-900 [&:hover]:bg-slate-800", href: "/articles/tag/\(tag)") {
div(class: "flex flex-row justify-between bg-slate-900 [&:hover]:bg-slate-800 py-2 px-4 border-2 border-orange-400 rounded-lg") {
div(class: "justify-items-start") {
span(class: "font-bold text-green-300") { tag }
div(class: "text-sm") {
span { "\(count) articles" }
}
}
img(src: "/static/img/tag.svg", width: "30")
}
}
}
}
}
}
}
}
func uniqueTagsWithCount(_ articles: [Item<ArticleMetadata>]) -> [(String, Int)] {
let tags = articles.flatMap { $0.metadata.tags }
let tagsWithCounts = tags.reduce(into: [:]) { $0[$1, default: 0] += 1 }
return tagsWithCounts.sorted { $0.1 > $1.1 }
}