Compare commits
4 commits
cc941860c8
...
afe12787c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afe12787c9 | ||
|
|
9e9514db8c | ||
|
|
b3625489d1 | ||
|
|
3bba57756b |
5 changed files with 110 additions and 3 deletions
|
|
@ -13,6 +13,8 @@ htmx = HTMX(app)
|
|||
|
||||
@app.template_filter()
|
||||
def format_datetime(value):
|
||||
if not value:
|
||||
return value
|
||||
return datetime.fromtimestamp(value).isoformat()
|
||||
|
||||
|
||||
|
|
|
|||
2
a5/requirements.txt
Normal file
2
a5/requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
flask
|
||||
flask-htmx
|
||||
102
a5/static/htmlx-smol.js
Normal file
102
a5/static/htmlx-smol.js
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
const elementTimers = new Map;
|
||||
|
||||
function addTimer(element, timerId) {
|
||||
const timers = elementTimers.get(element) || [];
|
||||
timers.push(timerId)
|
||||
elementTimers.set(element, timers);
|
||||
}
|
||||
function removeTimerElement(element) {
|
||||
const timers = elementTimers.get(element) || [];
|
||||
for (const timer of timers) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
elementTimers.delete(element);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
function handleEvent(e) {
|
||||
e.preventDefault();
|
||||
handleTrigger(e.target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} duration
|
||||
*/
|
||||
function parseDuration(duration) {
|
||||
const parsed = duration.match(/(\d+(?:\.\d+)?(s|ms))/);
|
||||
if (!parsed) throw new Error("Unknown duration");
|
||||
const num = parseFloat(parsed[1]);
|
||||
const type = parsed[2];
|
||||
const length = { "s": 1000, "ms": 1 };
|
||||
return num * length[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element|Document} element
|
||||
*/
|
||||
function registerHandlers(element) {
|
||||
const elements = element.querySelectorAll("[hx-get],[hx-trigger],[hx-target]");
|
||||
|
||||
for (const element of elements) {
|
||||
const trigger = element.getAttribute("hx-trigger");
|
||||
if (trigger) {
|
||||
const [key, duration, ...rest] = trigger.split(" ");
|
||||
if (rest.length) throw new Error("Unknown trigger: " + rest);
|
||||
const parsedDuration = parseDuration(duration);
|
||||
if (key !== "every") throw new Error("Unknown trigger: " + key);
|
||||
addTimer(element, setInterval(handleTrigger.bind(null, element), parsedDuration));
|
||||
} else {
|
||||
if ("onclick" in element) {
|
||||
element.onclick = handleEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
*/
|
||||
async function handleTrigger(element) {
|
||||
const replaceUrl = element.getAttribute("hx-replace-url");
|
||||
const get = element.getAttribute("hx-get");
|
||||
const targetSelector = element.getAttribute("hx-target");
|
||||
const targetElement = document.querySelector(targetSelector) || element;
|
||||
|
||||
if (replaceUrl && replaceUrl !== "false") {
|
||||
const targetUrl = replaceUrl == "true" ? get : replaceUrl;
|
||||
if (targetUrl) {
|
||||
window.history.replaceState({}, "", targetUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (get) {
|
||||
const res = await fetch(get, {
|
||||
headers: {
|
||||
"HX-Request": "true",
|
||||
"HX-Trigger": element.id,
|
||||
"HX-Target": targetSelector,
|
||||
"HX-Current-URL": document.location.href
|
||||
}
|
||||
});
|
||||
const data = await res.text();
|
||||
|
||||
if (!targetElement) {
|
||||
console.error("Target element not found! Attempted:", targetSelector);
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = [targetElement, ...targetElement.querySelectorAll("*")];
|
||||
for (const element of elements) {
|
||||
removeTimerElement(element);
|
||||
}
|
||||
|
||||
targetElement.innerHTML = data;
|
||||
|
||||
registerHandlers(targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = () => registerHandlers(document);
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
rel="stylesheet"
|
||||
>
|
||||
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="anonymous"></script>
|
||||
{#<script src="/static/htmlx-smol.js"></script>#}
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-gradient"></div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div hx-get="/measurements?htmx" hx-trigger="every 2s" hx-target="main"></div>
|
||||
<div hx-get="/measurements?page={{page}}&htmx" hx-trigger="every 2s" hx-target="main"></div>
|
||||
|
||||
<h1>Measurements</h1>
|
||||
<p>Page {{page}}</p>
|
||||
|
|
@ -21,10 +21,10 @@
|
|||
|
||||
<div>
|
||||
{% if page > 1 %}
|
||||
<a href="/measurements?page={{page-1}}" hx-get="/measurements?page={{page-1}}&htmx" hx-target="main" class="btn btn-link">Previous page</a>
|
||||
<a href="/measurements?page={{page-1}}" hx-replace-url="/measurements?page={{page-1}}" hx-get="/measurements?page={{page-1}}&htmx" hx-target="main" class="btn btn-link">Previous page</a>
|
||||
{% endif %}
|
||||
{% if has_next_page %}
|
||||
<a href="/measurements?page={{page+1}}" hx-get="/measurements?page={{page+1}}&htmx" hx-target="main" class="btn btn-link">Next page</a>
|
||||
<a href="/measurements?page={{page+1}}" hx-replace-url="/measurements?page={{page+1}}" hx-get="/measurements?page={{page+1}}&htmx" hx-target="main" class="btn btn-link">Next page</a>
|
||||
{% endif %}
|
||||
|
||||
<a href="/" class="btn btn-link">View statistics</a>
|
||||
|
|
|
|||
Loading…
Reference in a new issue