169 lines
4.5 KiB
Plaintext
169 lines
4.5 KiB
Plaintext
|
package layouts
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
|
||
|
"git.grosinger.net/tgrosinger/saasitone/pkg/page"
|
||
|
"git.grosinger.net/tgrosinger/saasitone/pkg/funcmap"
|
||
|
"git.grosinger.net/tgrosinger/saasitone/templ/components"
|
||
|
)
|
||
|
|
||
|
templ Main(p page.Page, content templ.Component) {
|
||
|
<!DOCTYPE html>
|
||
|
<html lang="en" style="height:100%;">
|
||
|
<head>
|
||
|
@metatags(p)
|
||
|
@css()
|
||
|
@js()
|
||
|
</head>
|
||
|
<body class="has-background-light" style="min-height:100%;">
|
||
|
<nav class="navbar is-dark">
|
||
|
<div class="container">
|
||
|
<div class="navbar-brand" hx-boost="true">
|
||
|
<a href={ templ.URL(p.ToURL("home")) } class="navbar-item">{ p.AppName }</a>
|
||
|
</div>
|
||
|
<div id="navbarMenu" class="navbar-menu">
|
||
|
<div class="navbar-end">
|
||
|
@search(p)
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</nav>
|
||
|
<div class="container mt-5">
|
||
|
<div class="columns">
|
||
|
<div class="column is-2">
|
||
|
<aside class="menu" hx-boost="true">
|
||
|
<p class="menu-label">General</p>
|
||
|
<ul class="menu-list">
|
||
|
<li>
|
||
|
@link(p, p.ToURL("home"), "Dashboard", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("about"), "About", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("contact"), "Contact", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("cache"), "Cache", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("task"), "Task", "")
|
||
|
</li>
|
||
|
</ul>
|
||
|
<p class="menu-label">Account</p>
|
||
|
<ul class="menu-list">
|
||
|
if p.IsAuth {
|
||
|
<li>
|
||
|
@link(p, p.ToURL("logout"), "Logout", "")
|
||
|
</li>
|
||
|
} else {
|
||
|
<li>
|
||
|
@link(p, p.ToURL("login"), "Login", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("register"), "Register", "")
|
||
|
</li>
|
||
|
<li>
|
||
|
@link(p, p.ToURL("forgot_password"), "Forgot password", "")
|
||
|
</li>
|
||
|
}
|
||
|
</ul>
|
||
|
</aside>
|
||
|
</div>
|
||
|
<div class="column is-10">
|
||
|
<div class="box">
|
||
|
if p.Title != "" {
|
||
|
<h1 class="title">{ p.Title }</h1>
|
||
|
}
|
||
|
@components.Messages(p)
|
||
|
@content
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
@footer(p)
|
||
|
</body>
|
||
|
</html>
|
||
|
}
|
||
|
|
||
|
templ link(p page.Page, url, text, classes string) {
|
||
|
<a class={ classes, templ.KV("is-active", p.Path == url) } href={ templ.URL(url) }>{ text }</a>
|
||
|
}
|
||
|
|
||
|
templ search(p page.Page) {
|
||
|
<div class="search mr-2 mt-1" x-data="{modal:false}">
|
||
|
<input class="input" type="search" placeholder="Search..." @click="modal = true; $nextTick(() => $refs.input.focus());"/>
|
||
|
<div class="modal" :class="modal ? 'is-active' : ''" x-show="modal == true">
|
||
|
<div class="modal-background"></div>
|
||
|
<div class="modal-content" @click.away="modal = false;">
|
||
|
<div class="box">
|
||
|
<h2 class="subtitle">Search</h2>
|
||
|
<p class="control">
|
||
|
<input
|
||
|
hx-get={ p.ToURL("search") }
|
||
|
hx-trigger="keyup changed delay:500ms"
|
||
|
hx-target="#results"
|
||
|
name="query"
|
||
|
class="input"
|
||
|
type="search"
|
||
|
placeholder="Search..."
|
||
|
x-ref="input"
|
||
|
/>
|
||
|
</p>
|
||
|
<div class="block"></div>
|
||
|
<div id="results"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<button class="modal-close is-large" aria-label="close"></button>
|
||
|
</div>
|
||
|
</div>
|
||
|
}
|
||
|
|
||
|
templ metatags(p page.Page) {
|
||
|
if p.Title != "" {
|
||
|
<title>{ p.AppName } | { p.Title }</title>
|
||
|
} else {
|
||
|
<title>{ p.AppName }</title>
|
||
|
}
|
||
|
<link rel="icon" href={ funcmap.File("favicon.png") }/>
|
||
|
<meta charset="utf-8"/>
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||
|
if p.Metatags.Description != "" {
|
||
|
<meta name="description" content="{{.Metatags.Description}}"/>
|
||
|
}
|
||
|
if len(p.Metatags.Keywords) > 0 {
|
||
|
<meta name="keywords" content={ strings.Join(p.Metatags.Keywords, ",") }/>
|
||
|
}
|
||
|
}
|
||
|
|
||
|
templ css() {
|
||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"/>
|
||
|
}
|
||
|
|
||
|
templ js() {
|
||
|
<script src="https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js"></script>
|
||
|
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||
|
}
|
||
|
|
||
|
templ footer(p page.Page) {
|
||
|
if p.CSRF != "" {
|
||
|
<script>
|
||
|
document.body.addEventListener('htmx:configRequest', function(evt) {
|
||
|
if (evt.detail.verb !== "get") {
|
||
|
evt.detail.parameters['csrf'] = {p.CSRF};
|
||
|
}
|
||
|
})
|
||
|
</script>
|
||
|
}
|
||
|
<script>
|
||
|
document.body.addEventListener('htmx:beforeSwap', function(evt) {
|
||
|
if (evt.detail.xhr.status >= 400){
|
||
|
evt.detail.shouldSwap = true;
|
||
|
evt.detail.target = htmx.find("body");
|
||
|
}
|
||
|
});
|
||
|
</script>
|
||
|
}
|