feat: add tag & filter system

This commit is contained in:
LordMZTE 2024-08-21 19:09:31 +02:00
parent f9c2addf99
commit 5c36b83939
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
13 changed files with 165 additions and 69 deletions

View file

@ -1,4 +1,4 @@
build: confgen.lua src resources hx/out/index.js hx/out/article.js
build: confgen.lua src meta resources hx/out/index.js hx/out/article.js
confgen confgen.lua build
hx/out/%.js: hx/%.hxml hx/src hx/common.hxml

View file

@ -1,59 +1,17 @@
import js.html.Element;
import index.Filter;
import js.Browser.window;
import js.Browser.document;
import lib.Article;
using Lambda;
import index.ArticleElement;
function main() {
loadArticles();
}
typedef Articles = {
var articles:Array<Article>;
};
/**
Adds the articles from the JSON index to the page.
**/
function loadArticles() {
final artdiv = document.getElementById("articles");
final filter = new Filter();
window
.fetch("/articles.json")
.then(r -> r.json())
.then((j:Articles) -> j.articles.iter(a -> artdiv.appendChild(makeArticleElement(a))));
}
/**
Creates an Element for an article to be added to the site.
**/
function makeArticleElement(art:Article):Element {
final aEl = document.createAnchorElement();
aEl.href = '/a/${art.id}/';
aEl.classList.add("article_a");
final divEl = document.createDivElement();
divEl.classList.add("article");
{
final rowEl = document.createDivElement();
rowEl.classList.add("row");
{
final headerEl = document.createElement("h2");
headerEl.innerText = art.title;
rowEl.appendChild(headerEl);
final dateEl = document.createParagraphElement();
dateEl.classList.add("date");
dateEl.innerText = art.date;
rowEl.appendChild(dateEl);
}
divEl.appendChild(rowEl);
final summaryEl = document.createParagraphElement();
summaryEl.innerText = art.summary;
divEl.appendChild(summaryEl);
}
aEl.appendChild(divEl);
return aEl;
.then((j:Array<Article>) -> {
filter.articles = j;
for (a in j) {
filter.articleDiv.appendChild(new ArticleElement(a));
}
});
}

View file

@ -0,0 +1,43 @@
package index;
import lib.Article;
import js.html.AnchorElement;
import js.Browser.document;
@:forward
abstract ArticleElement(AnchorElement) to AnchorElement {
public function new(art:Article) {
this = document.createAnchorElement();
this.href = '/a/${art.id}/';
this.classList.add("article_a");
final divEl = document.createDivElement();
divEl.classList.add("article");
{
final rowEl = document.createDivElement();
rowEl.classList.add("row");
{
final headerEl = document.createElement("h2");
headerEl.innerText = art.title;
rowEl.appendChild(headerEl);
final dateEl = document.createParagraphElement();
dateEl.classList.add("date");
dateEl.innerText = art.date;
rowEl.appendChild(dateEl);
}
divEl.appendChild(rowEl);
final summaryEl = document.createParagraphElement();
summaryEl.innerText = art.summary;
divEl.appendChild(summaryEl);
final tagsEl = document.createParagraphElement();
tagsEl.classList.add("tags");
tagsEl.innerText = art.tags.join(", ");
divEl.appendChild(tagsEl);
}
this.appendChild(divEl);
}
}

46
hx/src/index/Filter.hx Normal file
View file

@ -0,0 +1,46 @@
package index;
import lib.Article;
import js.html.DivElement;
import js.html.ButtonElement;
import js.Browser.document;
import js.Browser.window;
import js.html.InputElement;
using Lambda;
class Filter {
var bar:InputElement;
public var articleDiv:DivElement;
public var articles:Array<Article>;
public function new() {
this.bar = cast document.getElementById("filterbar");
this.articleDiv = cast document.getElementById("articles");
final btn:ButtonElement = cast document.getElementById("filterbtn");
btn.onclick = this.doFilter;
this.bar.onkeyup = ev -> if (ev.key == "Enter") this.doFilter();
}
function doFilter() {
if (this.articles == null) {
window.alert("Articles not yet received. Please wait!");
return;
}
// Clear articles
this.articleDiv.innerHTML = "";
final query = this.bar.value.split(" ");
for (article in this.articles) {
if (this.bar.value != "" && !query.foreach(t -> article.tags.contains(t.toLowerCase())))
continue;
// Add matching article back
this.articleDiv.appendChild(new ArticleElement(article));
}
}
}

View file

@ -6,4 +6,5 @@ abstract Article({
var id:String;
var summary:String;
var date:String;
var tags:Array<String>;
}) {}

View file

@ -14,6 +14,7 @@
</div>
<h1><% meta.title %></h1>
<p><% meta.summary %></p>
<p class="tags"><% table.concat(meta.tags, ", ") %></p>
<hr />
<div id="content">
<% opt.renderMarkdown(inner) %>

View file

@ -3,5 +3,5 @@ return {
title = "A Blog: better late than never",
summary = "I've got a blog now! One less place safe from my ramblings & infamous hot takes!",
date = "2024-08-16",
-- tags = {}, -- TODO
tags = { "code", "web", "confgen", "lua" },
}

View file

@ -116,17 +116,16 @@ to be found [here](/articles.json). It looks somewhat like this, but this is sub
without warning:
```json
{
"tags": {}, // This will likely include name & color of future tags
"articles": [ // Oldest first
{
"id": "0001-a-blog-better-late-than-never",
"title": "A Blog: better late than never",
"summary": "I've got a blog now! One less place safe from my ramblings & infamous hot takes!",
"date": "2024-08-16"
}
]
}
[ // Oldest first
{
"id": "0001-a-blog-better-late-than-never",
"title": "A Blog: better late than never",
"summary": "I've got a blog now! One less place safe from my ramblings & infamous hot takes!",
"date": "2024-08-16",
"tags": [ "code", "web", "confgen", "lua" ]
}
]
```
That's pretty much all there is to it. I hope you've enjoyed this quick read and here's to many more!

View file

@ -46,3 +46,8 @@ code,
float: right;
text-decoration: underline;
}
.tags {
color: #a6adc8;
font-style: italic;
}

View file

@ -1,4 +1 @@
<! local val = {
tags = {},
articles = opt.articles,
} !><% cg.fmt.json.serialize(val) %>
<% cg.fmt.json.serialize(opt.articles) %>

View file

@ -50,7 +50,7 @@
<rss version="2.0">
<channel>
<title>LordMZTE's Blog</title>
<description>A Repository of a nerd's thoughts and oppinions</description>
<description>A Repository of a nerd's thoughts and opinions</description>
<link>https://mzte.de</link>
<copyright>no lol</copyright>
<generator>Confgen</generator>

View file

@ -42,6 +42,43 @@ body {
text-decoration: underline;
}
.article .date,
.article h2 {
margin-top: 2px;
margin-bottom: 4px;
}
a {
color: #cba6f7;
}
.tags {
color: #a6adc8;
font-style: italic;
}
#filterdiv {
display: flex;
flex-direction: row;
}
#filterdiv * {
margin: 2px;
}
#filterbar {
flex-grow: 1;
}
#filterbar,
#filterbtn {
color: #cdd6f4;
background-color: #313244;
border: 1px solid;
border-color: #94e2d5;
}
#filterbtn:hover {
background-color: #585b70;
cursor: pointer;
}

View file

@ -48,7 +48,16 @@ If you want to chat, you can also reach me on matrix (`@lordmzte:mzte.de`).
<h1>Articles</h1>
<a href="/feed.rss">[RSS Feed]</a>
<div id="filterdiv">
<input type="text" id="filterbar" placeholder="filter space-separated tags"></input>
<button id="filterbtn">Filter</button>
</div>
<hr />
<noscript>
No JavaScript! Good choice for your sanity! My website mostly works without JS,
but you won't see articles listed here. Please see the <a href="/articles.json">JSON index</a>
and navigate to https://mzte.de/a/$ID/, substituting $ID for the id field from the JSON file.
</noscript>
<div id="articles">
</div>
<script src="index.js"></script>