added htmx swapping and url history features
This commit is contained in:
parent
7b9a760e67
commit
f97b31093c
7 changed files with 184 additions and 36 deletions
12
web/dashboard.templ
Normal file
12
web/dashboard.templ
Normal 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>
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"html/template"
|
|
||||||
|
|
||||||
"context"
|
"bytes"
|
||||||
|
"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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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>
|
||||||
<nav>
|
|
||||||
<button class="title" href="/">Recount!</button>
|
|
||||||
<ul>
|
|
||||||
<li><button hx-boost="true" class="active" href="/home">Home!</button></li>
|
|
||||||
<li><button href="/nohome">Not home!</button></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
</header>
|
||||||
<div id="main-body-content">
|
<div id="below-header">
|
||||||
<h1>Hello World!</h1>
|
|
||||||
<button hx-get="/hello" hx-trigger="click" hx-target="#hello" hx-swap="outerHtml">Hello you!</button>
|
|
||||||
|
|
||||||
<div id="hello">
|
<div id="left-col">
|
||||||
|
<nav>
|
||||||
|
<button class="title" href="/">Recount!</button>
|
||||||
|
<ul>
|
||||||
|
<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>
|
||||||
|
|
||||||
</div>
|
</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>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="main-body-content">
|
||||||
|
{{.InnerHtml}}
|
||||||
|
</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>
|
||||||
|
|
Loading…
Reference in a new issue