diff --git a/src/main.rs b/src/main.rs index 637df12..0e28e5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use std::io; +use std::{io, time::Duration}; use actix_files as fs; -use actix_web::{get, middleware, App, HttpRequest, HttpServer, Responder}; +use actix_web::{get, middleware, App, HttpRequest, HttpServer, Responder, rt::time::sleep}; use actix_web_lab::respond::Html; use askama::Template; @@ -44,9 +44,11 @@ async fn users(req: HttpRequest) -> actix_web::Result { email: "joe.doe@foo.baz".into(), }, ]); + match req.headers().get("hx-request") { Some(_) => { // Render the hypermedia fragment. + sleep(Duration::from_millis(1000)).await; Ok(Html(Fragment { users }.render().expect("Valid template"))) } None => { diff --git a/static/css/style.css b/static/css/style.css index 03d0aca..484c075 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,3 +1,89 @@ h1 { color: blue; } + +[data-loader] { + position: fixed; + top: 0; + left: 0; + z-index: 1000; + height: .2em; + padding: 0; + margin: 0; + background-color: blue; + transition: all 500ms; +} + +body { + display: grid; + grid-template-columns: 15em 1fr; + grid-template-rows: 3em 1fr 3em; + + grid-template-areas: + "nav header" + "nav main" + "nav footer"; + + margin: 0; + height: 100vh; +} + +@media (max-width: 1080px) { + body { + grid-template-columns: 1fr; + grid-template-rows: 3em 1fr 3em; + + grid-template-areas: + "header" + "main" + "footer"; + } + + nav { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 15em; + } +} + +body > * { + padding: 1em; +} + +hgroup * { + margin: 0; +} + +nav { + grid-area: nav; + + background-color: lightgray; +} + +header { + grid-area: header; + + background-color: orange; + + display: flex; + justify-content: space-evenly; + align-items: center; +} + +main { + grid-area: main; + + background-color: darkgray; +} + +footer { + grid-area: footer; + + background-color: grey; + + display: flex; + justify-content: flex-start; + align-items: center; +} diff --git a/static/js/loader.js b/static/js/loader.js new file mode 100644 index 0000000..0f3d576 --- /dev/null +++ b/static/js/loader.js @@ -0,0 +1,98 @@ +/** + * A loading bar that hooks into htmx events to automatically indicate the + * progression of pending requests. + * + * ```html + *
+ * ``` + * Only the first loader will be used. + */ +(function () { + var MAX_INCREMENT = 20; + var MIN_INCREMENT = 3; + var ADVANCE_UNTIL = 90; + + var pending = 0; + var increment = MAX_INCREMENT; + var timeout = null; + var interval = null; + + function getLoader() { + return document.querySelector("[data-loader]"); + } + + function clamp(x, a, b) { + return Math.max(a, Math.min(x, b)); + } + + function progress(loader) { + loader.hidden = false; + var current = parseInt(loader.dataset.loader); + + if (current < ADVANCE_UNTIL) { + current = Math.min(current + increment, ADVANCE_UNTIL); + loader.dataset.loader = current; + loader.style.right = 100 - current + "%"; + increment = clamp(Math.floor(increment * 0.65), MIN_INCREMENT, MAX_INCREMENT); + } + } + + function start() { + pending++; + + if (pending > 1) { + return; + } + + clearTimeout(timeout); + clearInterval(interval); + + increment = MAX_INCREMENT; + + var loader = getLoader(); + if (!loader) { + return; + } + + // Give the request a reasonable time to complete before triggering the + // loader. + timeout = setTimeout(function () { + loader.dataset.loader = 0; + loader.style.right = "100%"; + loader.hidden = false; + + // Increment the loader state until it reaches the threshold. + // Trickery and lies! + interval = setInterval(function () { + progress(loader) + }, 100); + }, 250); + } + + function finish() { + pending--; + + if (pending <= 0) { + clearTimeout(timeout); + clearInterval(interval); + + var loader = getLoader(); + if (!loader) { + return; + } + + loader.style.right = 0; + timeout = setTimeout(function () { + loader.hidden = true; + timeout = setTimeout(function () { + loader.dataset.loader = 0; + loader.style.right = "100%"; + }, 500); + }, 500); + return; + } + } + + addEventListener("htmx:beforeRequest", start); + addEventListener("htmx:afterRequest", finish); +})(); diff --git a/templates/layout.html b/templates/layout.html index 47a0a21..5ef41c4 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -7,15 +7,34 @@ - + + {% block title %}Hello htmx!{% endblock %} {% block head %}{% endblock %} + + +
+
Item 1
+
Item 2
+
Item 3
+
{% block content %}{% endblock %}
- +