package templates
import (
"io"
"alin.ovh/homestead/shared/config"
"github.com/Southclaws/fault"
"github.com/alanpearce/htmlformat"
g "alin.ovh/gomponents"
. "alin.ovh/gomponents/html"
)
type SiteSettings struct {
Title string
Language string
Timezone config.Timezone
Menu []config.MenuItem
InjectLiveReload bool
}
type Attrs map[string]string
type PageSettings struct {
Title string
TitleAttrs Attrs
BodyAttrs Attrs
HeadExtra []g.Node
}
var LiveReload = Script(Defer(), g.Raw(`
new EventSource("/_/reload").onmessage = event => {
console.log("got message", event)
window.location.reload()
};
`))
func ExtendAttrs(base Attrs, attrs Attrs) g.Node {
m := base
for key, value := range attrs {
if v, found := base[key]; found {
m[key] = v + " " + value
} else {
m[key] = value
}
}
return g.MapMap(m, func(k string, v string) g.Node {
return g.Attr(k, v) // can't inline this because it uses ...value, grr
})
}
func Layout(site SiteSettings, page PageSettings, children ...g.Node) g.Node {
return DoctypeHTML(FormattedDocument(
HTML(
Lang(site.Language),
Head(
Meta(Name("viewport"), Content("width=device-width, initial-scale=1.0")),
TitleEl(g.Text(page.Title)),
Link(
Rel("stylesheet"),
Type("text/css"),
Href("/style.css"),
),
Link(
Rel("alternate"),
Type("application/atom+xml"),
Title(site.Title),
Href("/atom.xml"),
),
g.Group(page.HeadExtra),
),
Body(
ExtendAttrs(page.BodyAttrs, nil),
A(Class("skip"), Href("#main"), g.Text("Skip to main content")),
Header(
H2(
A(
ExtendAttrs(Attrs{
"class": "title p-name",
"href": "/",
}, page.TitleAttrs),
g.Text(site.Title)),
),
Nav(
g.Map(site.Menu, MenuLink),
),
),
Main(ID("main"), g.Group(children)),
Footer(
A(Href("https://git.alin.ovh/website"), g.Text("Content")),
g.Text(" is "),
A(
Rel("license"),
Href("http://creativecommons.org/licenses/by/4.0/"),
g.Text("CC BY 4.0"),
),
g.Text(". "),
A(Href("https://git.alin.ovh/homestead"), g.Text("Site source code")),
g.Text(" is "),
A(Href("https://opensource.org/licenses/MIT"), g.Text("MIT")),
),
g.If(site.InjectLiveReload,
LiveReload,
),
),
)))
}
func MenuLink(item config.MenuItem) g.Node {
return A(
Href(item.URL.String()),
g.If(item.URL.IsAbs(), Target("_blank")),
g.Text(item.Name),
)
}
func FormattedDocument(doc g.Node) g.Node {
return g.NodeFunc(func(w io.Writer) error {
pr, pw := io.Pipe()
defer pr.Close()
go func() {
pw.CloseWithError(doc.Render(pw))
}()
if err := htmlformat.Document(w, pr); err != nil {
return fault.Wrap(err)
}
return nil
})
}
func DoctypeHTML(sibling g.Node) g.Node {
return g.NodeFunc(func(w io.Writer) error {
if _, err := w.Write([]byte("\n")); err != nil {
return err
}
return sibling.Render(w)
})
}
domain/web/templates/layout.go (view raw)