broken but cool htmx! also improved templating

Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
ari melody 2024-03-22 07:30:22 +00:00
parent 5c59348362
commit c1ff03c4e5
18 changed files with 297 additions and 97 deletions

View file

@ -1,4 +1,3 @@
const header_home = document.getElementById("header-home");
const header_links = document.getElementById("header-links");
const hamburger = document.getElementById("header-links-toggle");
@ -7,14 +6,9 @@ function toggle_header_links() {
}
document.addEventListener("click", event => {
if (!header_links.contains(event.target) && !hamburger.contains(event.target)) {
if (!header_links.contains(event.target) && !hamburger.contains(event.target) && !header_links.href) {
header_links.classList.remove("open");
}
});
hamburger.addEventListener("click", event => { toggle_header_links(); });
header_home.addEventListener("click", event => {
event.preventDefault();
location.href = "/";
});

View file

@ -0,0 +1,141 @@
//==========================================================
// head-support.js
//
// An extension to htmx 1.0 to add head tag merging.
//==========================================================
(function(){
var api = null;
function log() {
//console.log(arguments);
}
function mergeHead(newContent, defaultMergeStrategy) {
if (newContent && newContent.indexOf('<head') > -1) {
const htmlDoc = document.createElement("html");
// remove svgs to avoid conflicts
var contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '');
// extract head tag
var headTag = contentWithSvgsRemoved.match(/(<head(\s[^>]*>|>)([\s\S]*?)<\/head>)/im);
// if the head tag exists...
if (headTag) {
var added = []
var removed = []
var preserved = []
var nodesToAppend = []
htmlDoc.innerHTML = headTag;
var newHeadTag = htmlDoc.querySelector("head");
var currentHead = document.head;
if (newHeadTag == null) {
return;
} else {
// put all new head elements into a Map, by their outerHTML
var srcToNewHeadNodes = new Map();
for (const newHeadChild of newHeadTag.children) {
srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
}
}
// determine merge strategy
var mergeStrategy = api.getAttributeValue(newHeadTag, "hx-head") || defaultMergeStrategy;
// get the current head
for (const currentHeadElt of currentHead.children) {
// If the current head element is in the map
var inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
var isReAppended = currentHeadElt.getAttribute("hx-head") === "re-eval";
var isPreserved = api.getAttributeValue(currentHeadElt, "hx-preserve") === "true";
if (inNewContent || isPreserved) {
if (isReAppended) {
// remove the current version and let the new version replace it and re-execute
removed.push(currentHeadElt);
} else {
// this element already exists and should not be re-appended, so remove it from
// the new content map, preserving it in the DOM
srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
preserved.push(currentHeadElt);
}
} else {
if (mergeStrategy === "append") {
// we are appending and this existing element is not new content
// so if and only if it is marked for re-append do we do anything
if (isReAppended) {
removed.push(currentHeadElt);
nodesToAppend.push(currentHeadElt);
}
} else {
// if this is a merge, we remove this content since it is not in the new head
if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: currentHeadElt}) !== false) {
removed.push(currentHeadElt);
}
}
}
}
// Push the tremaining new head elements in the Map into the
// nodes to append to the head tag
nodesToAppend.push(...srcToNewHeadNodes.values());
log("to append: ", nodesToAppend);
for (const newNode of nodesToAppend) {
log("adding: ", newNode);
var newElt = document.createRange().createContextualFragment(newNode.outerHTML);
log(newElt);
if (api.triggerEvent(document.body, "htmx:addingHeadElement", {headElement: newElt}) !== false) {
currentHead.appendChild(newElt);
added.push(newElt);
}
}
// remove all removed elements, after we have appended the new elements to avoid
// additional network requests for things like style sheets
for (const removedElement of removed) {
if (api.triggerEvent(document.body, "htmx:removingHeadElement", {headElement: removedElement}) !== false) {
currentHead.removeChild(removedElement);
}
}
api.triggerEvent(document.body, "htmx:afterHeadMerge", {added: added, kept: preserved, removed: removed});
}
}
}
htmx.defineExtension("head-support", {
init: function(apiRef) {
// store a reference to the internal API.
api = apiRef;
htmx.on('htmx:afterSwap', function(evt){
var serverResponse = evt.detail.xhr.response;
if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
mergeHead(serverResponse, evt.detail.boosted ? "merge" : "append");
}
})
htmx.on('htmx:historyRestore', function(evt){
if (api.triggerEvent(document.body, "htmx:beforeHeadMerge", evt.detail)) {
if (evt.detail.cacheMiss) {
mergeHead(evt.detail.serverResponse, "merge");
} else {
mergeHead(evt.detail.item.head, "merge");
}
}
})
htmx.on('htmx:historyItemCreated', function(evt){
var historyItem = evt.detail.item;
historyItem.head = document.head.outerHTML;
})
}
});
})()

View file

@ -63,3 +63,17 @@ document.addEventListener("htmx:afterSwap", async event => {
}
window.scrollY = 0;
});
const top_button = document.getElementById("backtotop");
window.onscroll = () => {
if (!top_button) return;
const btt_threshold = 100;
if (
document.body.scrollTop > btt_threshold ||
document.documentElement.scrollTop > btt_threshold
) {
top_button.classList.add("active");
} else {
top_button.classList.remove("active");
}
}

View file

@ -12,7 +12,9 @@ share_btn.onclick = (e) => {
share_btn.classList.add('active');
}
document.getElementById("go-back").addEventListener("click", () => {
const go_back_btn = document.getElementById("go-back")
go_back_btn.innerText = "<";
go_back_btn.addEventListener("click", () => {
window.history.back();
});

View file

@ -1,15 +1,5 @@
document.querySelectorAll("h2.question").forEach(element => {
element.onclick = (e) => {
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}#${e.target.id}`;
window.location = url;
};
});
import "./main.js";
document.querySelectorAll("div.music").forEach(element => {
console.log(element);
element.addEventListener("click", (e) => {
const url = `${window.location.protocol}//${window.location.host}/music/${element.id}`;
window.location = url;
});
document.querySelectorAll("h1.music-title").forEach(element => {
element.href = "";
});

View file

@ -1,6 +1,7 @@
@import url("/style/colours.css");
@import url("/style/header.css");
@import url("/style/footer.css");
@import url("/style/prideflag.css");
@font-face {
font-family: "Monaspace Argon";
@ -42,6 +43,32 @@ span.newchar {
animation: newchar 0.25s;
}
a#backtotop {
position: fixed;
left: 50%;
transform: translateX(-50%);
padding: .5em .8em;
display: block;
border-radius: 2px;
border: 1px solid transparent;
text-decoration: none;
opacity: .5;
transition-property: opacity, transform, border-color, background-color, color;
transition-duration: .2s;
}
a#backtotop.active {
top: 4rem;
}
a#backtotop:hover {
color: #eee;
border-color: #eee;
background-color: var(--links);
box-shadow: 0 0 1em var(--links);
opacity: 1;
}
@keyframes newchar {
from {
background: #fff8;

View file

@ -241,7 +241,7 @@ div#info p {
#title {
margin: 0;
line-height: 1em;
font-size: 3em;
font-size: 2.5em;
}
#year {
@ -571,6 +571,7 @@ footer a:hover {
}
div#info > div {
min-width: auto;
min-height: auto;
padding: 0;
margin: 0;

View file

@ -1,5 +1,12 @@
@import url("/style/index.css");
main {
width: min(calc(100% - 4rem), 720px);
min-height: calc(100vh - 10.3rem);
margin: 0 auto 2rem auto;
padding-top: 4rem;
}
div.music {
margin-bottom: 1rem;
padding: 1.5rem;
@ -114,7 +121,7 @@ h2.question {
cursor: pointer;
}
.collapse {
div.answer {
margin: -1rem 0 1rem 0;
padding: .5em 1.5em;
border-radius: 4px;

View file

@ -0,0 +1,25 @@
#prideflag svg {
position: fixed;
top: 0;
right: 0;
width: 120px;
transform-origin: 100% 0%;
transition: transform .5s cubic-bezier(.32,1.63,.41,1.01);
z-index: 8008135;
pointer-events: none;
}
#prideflag svg:hover {
transform: scale(110%);
}
#prideflag svg:active {
transform: scale(110%);
}
#prideflag svg * {
pointer-events: all;
}
@media screen and (max-width: 950px) {
#prideflag {
display: none;
}
}