added htmx swapping and url history features

This commit is contained in:
Nickiel12 2024-01-13 18:33:26 -08:00
parent 7b9a760e67
commit f97b31093c
7 changed files with 184 additions and 36 deletions

12
web/dashboard.templ Normal file
View file

@ -0,0 +1,12 @@
package web
templ dashboard() {
<title>Home</title>
<h1>Hello World!</h1>
<button hx-get="/hello" hx-trigger="click" hx-target="#hello" hx-swap="outerHtml">Hello you!</button>
<div id="hello">
</div>
}

View file

@ -1,6 +1,7 @@
package web package web
templ hello(name string) { templ hello(name string) {
<title>Hello</title>
<div>Hello { name } </div> <div>Hello { name } </div>
<h1>SUPRISE!!!</h1> <h1>SUPRISE!!!</h1>
} }

View file

@ -1,22 +1,25 @@
package web package web
import ( import (
"net/http"
"html/template" "html/template"
"net/http"
"bytes"
"context" "context"
"github.com/a-h/templ"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
const TemplateDir = "./web/templates/" type TemplateState struct {
InnerHtml template.HTML
type IndexData struct { ActivePage string
Title string
} }
const TemplateDir = "./web/templates/"
func SetLogLevel(level zerolog.Level) { func SetLogLevel(level zerolog.Level) {
zerolog.SetGlobalLevel(level) zerolog.SetGlobalLevel(level)
} }
@ -29,7 +32,23 @@ func WebRouter() http.Handler {
return r return r
} }
func getIndex(w http.ResponseWriter, req *http.Request) { //for name, values := range req.Header {
// Loop over all values for the name.
// for _, value := range values {
// log.Debug().Msg(name + " " + value);
// }
// }
func renderFullPage(w http.ResponseWriter, c templ.Component, pageName string) {
var buf bytes.Buffer
// Render the provided templ component
err := c.Render(context.Background(), &buf)
if err != nil {
log.Fatal().Err(err).Msg("Fatal error reading hello template");
}
// get the index template
index, err := template.ParseFiles(TemplateDir + "index.html") index, err := template.ParseFiles(TemplateDir + "index.html")
if err != nil { if err != nil {
log.Fatal(). log.Fatal().
@ -37,11 +56,40 @@ func getIndex(w http.ResponseWriter, req *http.Request) {
Msg("Fatal error reading index template") Msg("Fatal error reading index template")
} }
err = index.Execute(w, IndexData{Title: "thetitle"}) // Inject the templ component html into the index template
err = index.Execute(w,
TemplateState{
InnerHtml: template.HTML(buf.String()),
ActivePage: pageName,
});
if err != nil {
log.Fatal().Err(err).Msg("Fatal error reading hello template");
}
}
func getIndex(w http.ResponseWriter, req *http.Request) {
component := dashboard();
_, ok := req.Header["Hx-Request"]
if ok {
err := component.Render(context.Background(), w);
if err != nil {
log.Fatal().Err(err).Msg("Couldn't render dashboard templ template");
}
} else {
renderFullPage(w, component, "index");
}
} }
func getHello(w http.ResponseWriter, req *http.Request) { func getHello(w http.ResponseWriter, req *http.Request) {
log.Debug().Msg("Got index")
component := hello("Nick") component := hello("Nick")
component.Render(context.Background(), w) _, ok := req.Header["Hx-Request"]
if ok {
err := component.Render(context.Background(), w);
if err != nil {
log.Fatal().Err(err).Msg("Couldn't render dashboard templ template");
}
}else {
renderFullPage(w, component, "hello");
}
} }

View file

@ -1,30 +1,42 @@
header {
background-color: $nav-bg;
height: 30px;
width: 100%;
margin-top: 10px;
margin-bottom: 10px;
}
nav { nav {
background-color: $nav-bg; background-color: $nav-bg;
display: flex; display: flex;
flex-direction: column;
ul { ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
padding-left: 0;
} }
li { display: inline-block; } li {
margin-top: 10px;
button.title { margin-bottom: 10px;
color: $green;
} }
button.active { a.active {
pointer-events: none;
background-color: $nav-active-bg; background-color: $nav-active-bg;
color: $nav-active-color; color: $nav-active-color;
} }
button { a {
transition: background-color $hl-trn-spd ease-in, color $hl-trn-spd ease-in;
border-radius: 5px;
text-align: center;
padding: 12px 0px;
display: inline-block;
cursor: pointer;
color: $nav-color; color: $nav-color;
display: block; width: 100%;
padding: 12px 24px;
text-decoration: none; text-decoration: none;
@include button_link;
} }
button:hover { a:hover {
background-color: $nav-hover; background-color: $nav-hover;
} }
} }

View file

@ -1,5 +1,8 @@
@import 'catpuccin'; @import 'catpuccin';
// highlight transition speed
$hl-trn-spd: 0.2s;
$bg: $latte-base; $bg: $latte-base;
$nav-bg: $latte-crust; $nav-bg: $latte-crust;
$nav-color: $latte-text; $nav-color: $latte-text;
@ -21,3 +24,31 @@ body.dark-mode {
$nav-bg: $macchiato-crust; $nav-bg: $macchiato-crust;
$green: $macchiato-green; $green: $macchiato-green;
} }
div#below-header {
display:flex;
}
div#left-col {
width: 15vw;
padding: 10px;
}
div#main-body-content {
width: 70vw;
padding: 10px;
opacity: 1;
transition: opacity 0.2s ease-out;
}
div#main-body-content.htmx-swapping {
opacity: 0;
transition: opacity 0.2s ease-out;
}
div#main-body-content.htmx-added {
opacity: 0;
}
div#right-col {
width: 15vw;
padding: 10px;
}

View file

@ -3,4 +3,20 @@ function say_hello() {
console.log("Hello World"); console.log("Hello World");
} }
export {say_hello}; function register_nav_links() {
var navAnchors = document.querySelectorAll("nav a");
navAnchors.forEach(function (a_el) {
a_el.addEventListener("click", nav_a_click);
});
}
function nav_a_click(event) {
var active_anchor = document.querySelector("nav a.active");
active_anchor.classList.remove("active");
var clicked = event.target;
clicked.classList.add("active");
}
export {say_hello, register_nav_links};

View file

@ -5,30 +5,58 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/static/site.css"> <link rel="stylesheet" href="/static/site.css">
<script src="/static/htmx.min.js"></script> <script src="/static/htmx.min.js"></script>
<title>{{.Title}}</title>
</head> </head>
<body> <body>
<header> <header>
</header>
<div id="below-header">
<div id="left-col">
<nav> <nav>
<button class="title" href="/">Recount!</button> <button class="title" href="/">Recount!</button>
<ul> <ul>
<li><button hx-boost="true" class="active" href="/home">Home!</button></li> <li>
<li><button href="/nohome">Not home!</button></li> <a
hx-get="/"
hx-swap="innerHtml swap:0.2s settle:0.2s"
hx-push-url="true"
hx-boost="true"
hx-target="#main-body-content"
{{ if (eq .ActivePage "index") }}
class="active"
{{ end }}
>
Home!
</a>
</li>
<li>
<a
hx-get="/hello"
hx-swap="innerHtml swap:0.2s settle:0.2s"
hx-push-url="true"
hx-boost="true"
hx-target="#main-body-content"
{{ if (eq .ActivePage "hello") }}
class="active"
{{ end }}
>
Not home!
</a>
</li>
</ul> </ul>
</nav> </nav>
</header> </div>
<div id="main-body-content"> <div id="main-body-content">
<h1>Hello World!</h1> {{.InnerHtml}}
<button hx-get="/hello" hx-trigger="click" hx-target="#hello" hx-swap="outerHtml">Hello you!</button>
<div id="hello">
</div> </div>
</div> </div>
<script type="module" src="/static/index.js"></script> <script type="module" src="/static/index.js"></script>
<script type="module"> <script type="module">
import {say_hello} from "/static/index.js"; import {say_hello, register_nav_links} from "/static/index.js";
say_hello(); say_hello();
register_nav_links();
</script> </script>
</body> </body>
</html> </html>