feat: add tag & filter system
This commit is contained in:
parent
f9c2addf99
commit
5c36b83939
13 changed files with 165 additions and 69 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
43
hx/src/index/ArticleElement.hx
Normal file
43
hx/src/index/ArticleElement.hx
Normal 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
46
hx/src/index/Filter.hx
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,4 +6,5 @@ abstract Article({
|
|||
var id:String;
|
||||
var summary:String;
|
||||
var date:String;
|
||||
var tags:Array<String>;
|
||||
}) {}
|
||||
|
|
1
lib.cgt
1
lib.cgt
|
@ -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) %>
|
||||
|
|
|
@ -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" },
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -46,3 +46,8 @@ code,
|
|||
float: right;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.tags {
|
||||
color: #a6adc8;
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
<! local val = {
|
||||
tags = {},
|
||||
articles = opt.articles,
|
||||
} !><% cg.fmt.json.serialize(val) %>
|
||||
<% cg.fmt.json.serialize(opt.articles) %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue