Merge pull request #1 from anhgelus/feat/custom-page

[Feat] Custom page
This commit is contained in:
William Hergès 2024-12-10 20:47:56 +01:00 committed by GitHub
commit 0149f9905c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 323 additions and 19 deletions

View file

@ -176,6 +176,9 @@
"image",
"tags"
]
},
"custom_pages": {
"type": "array"
}
},
"additionalProperties": false,

117
custom_page.schema.json Normal file
View file

@ -0,0 +1,117 @@
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"uri": {
"type": "string"
},
"image": {
"type": "string"
},
"description": {
"type": "string"
},
"colors": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"tag_hover": {
"type": "string"
},
"background": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"angle": {
"type": "number"
},
"colors": {
"type": "array",
"items": {
"type": "object",
"properties": {
"color": {
"type": "string"
},
"position": {
"type": "number"
}
},
"required": [
"color",
"position"
]
}
}
},
"required": [
"type",
"angle",
"colors"
]
},
"buttons": {
"type": "object",
"properties": {
"text": {
"type": "string"
},
"text_hover": {
"type": "string"
},
"background": {
"type": "string"
},
"background_hover": {
"type": "string"
}
},
"required": [
"text",
"text_hover",
"background",
"background_hover"
]
}
},
"required": [
"text",
"tag_hover",
"background",
"buttons"
]
},
"content": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"content": {
"type": "string"
}
},
"required": [
"type",
"content"
]
}
}
},
"required": [
"title",
"uri",
"image",
"description",
"colors",
"content"
]
}

160
data.go
View file

@ -1,11 +1,31 @@
package main
import (
"encoding/json"
"fmt"
"github.com/anhgelus/golatt"
"html/template"
"log/slog"
"os"
"strconv"
"strings"
)
const (
TitleContentType = "title"
SubtitleContentType = "subtitle"
ParagraphContentType = "paragraph"
ListContentType = "list"
OrderedListContentType = "ordered_list"
ButtonsContentType = "links"
)
type ConfigData interface {
GetTextColor() template.CSS
GetBackground() template.CSS
GetBackgroundImage() template.CSS
}
type Config struct {
Image string `json:"image"`
Description string `json:"description"`
@ -13,6 +33,7 @@ type Config struct {
Color *Color `json:"colors"`
Links []*Link `json:"links"`
Legal *Legal `json:"legal"`
CustomPages []string `json:"custom_pages"`
}
type Person struct {
@ -63,7 +84,62 @@ type Legal struct {
}
func (c *Config) GetBackground() template.CSS {
bg := c.Color.Background
return c.Color.GetBackground()
}
func (c *Config) GetBackgroundImage() template.CSS {
return template.CSS("--background-image: url(" + golatt.GetStaticPath(c.Image) + ");")
}
func (c *Config) GetTextColor() template.CSS {
return c.Color.GetTextColor()
}
type CustomPage struct {
Title string `json:"title"`
URI string `json:"uri"`
Image string `json:"image"`
Description string `json:"description"`
Color *Color `json:"colors"`
Content []*CustomContent `json:"content"`
}
type CustomContent struct {
Type string `json:"type"`
Content string `json:"content"`
}
type Content interface {
Get() template.HTML
}
func (c *Config) LoadCustomPages() ([]*CustomPage, error) {
if c.CustomPages == nil {
println("null")
return nil, nil
}
var pages []*CustomPage
for _, cp := range c.CustomPages {
b, err := os.ReadFile(cp)
if err != nil {
return nil, err
}
var p CustomPage
err = json.Unmarshal(b, &p)
if err != nil {
return nil, err
}
pages = append(pages, &p)
}
return pages, nil
}
func (t *Color) GetTextColor() template.CSS {
return template.CSS("--text-color: " + t.Text + ";")
}
func (t *Color) GetBackground() template.CSS {
bg := t.Background
css := "background: " + bg.Type + "-gradient("
if bg.Type == "linear" {
css += strconv.Itoa(int(bg.Angle)) + "deg,"
@ -74,14 +150,6 @@ func (c *Config) GetBackground() template.CSS {
return template.CSS(css[:len(css)-1] + ");")
}
func (c *Config) GetBackgroundImage() template.CSS {
return template.CSS("--background-image: url(" + golatt.GetStaticPath(c.Image) + ");")
}
func (c *Config) GetTextColor() template.CSS {
return template.CSS("--text-color: " + c.Color.Text + ";")
}
func (b *ButtonColor) GetTextColor() template.CSS {
return template.CSS("--text-color: " + b.Text + ";--text-color-hover: " + b.TextHover + ";")
}
@ -93,3 +161,77 @@ func (b *ButtonColor) GetBackground() template.CSS {
func (t *Color) GetTagColor() template.CSS {
return template.CSS("--tag-hover: " + t.TagHover + ";")
}
func (p *CustomPage) GetTextColor() template.CSS {
return p.Color.GetTextColor()
}
func (p *CustomPage) GetBackgroundImage() template.CSS {
return template.CSS("--background-image: url(" + golatt.GetStaticPath(p.Image) + ");")
}
func (p *CustomPage) GetBackground() template.CSS {
return p.Color.GetBackground()
}
func (p *CustomPage) GetContent() template.HTML {
var res template.HTML
for _, c := range p.Content {
res += c.Get(p)
}
return res
}
func (c *CustomContent) Get(p *CustomPage) template.HTML {
if c.Type == TitleContentType {
return template.HTML("<h2>" + c.Content + "</h2>")
} else if c.Type == SubtitleContentType {
return template.HTML("<h3>" + c.Content + "</h3>")
} else if c.Type == ParagraphContentType {
return template.HTML("<p>" + c.Content + "</p>")
} else if c.Type == ListContentType {
v := ""
for _, s := range strings.Split(c.Content, "--") {
if len(strings.Trim(s, " ")) == 0 {
continue
}
v += "<li>" + strings.Trim(s, " ") + "</li>"
}
return template.HTML("<ul>" + v + "</ul>")
} else if c.Type == OrderedListContentType {
v := ""
for _, s := range strings.Split(c.Content, "--") {
if len(strings.TrimSpace(s)) == 0 {
continue
}
v += "<li>" + strings.TrimSpace(s) + "</li>"
}
return template.HTML("<ol>" + v + "</ol>")
} else if c.Type == ButtonsContentType {
// [Bonsoir](/hello) -- [Bonjour](/not_hello)
v := ""
for _, s := range strings.Split(c.Content, "--") {
if len(strings.TrimSpace(s)) == 0 {
continue
}
sp := strings.Split(s, "](")
if len(sp) != 2 {
slog.Warn("Invalid button", "s", s)
continue
}
url := strings.TrimSpace(sp[1])
v += fmt.Sprintf(
`<div class="link"><a href="%s">%s</a></div>`,
url[:len(url)-1],
strings.TrimSpace(sp[0])[1:],
)
}
return template.HTML(fmt.Sprintf(
`<nav class="links" style="%s">%s</nav>`,
p.Color.Button.GetBackground()+p.Color.Button.GetTextColor(),
v,
))
}
slog.Warn("Unknown type", "type", c.Type, "value", c.Content)
return ""
}

View file

@ -23,10 +23,7 @@ function changePage(href: string) {
const doc = new DOMParser().parseFromString(html, "text/html")
window.history.pushState({}, "", href)
document.title = doc.title
const distMain = doc.querySelector("main")
const currentMain = document.querySelector("main")
if (distMain === null || currentMain === null) document.body = doc.body
else currentMain.innerHTML = distMain.innerHTML
document.body = doc.body
setupEvents()
scrollTo({
top: 0,

15
main.go
View file

@ -50,6 +50,10 @@ func main() {
if err != nil {
panic(err)
}
customPages, err := cfg.LoadCustomPages()
if err != nil {
panic(err)
}
g := golatt.New(templates)
g.DefaultSeoData = &golatt.SeoData{
Image: cfg.Image,
@ -74,6 +78,17 @@ func main() {
&cfg).
Handle()
for _, cp := range customPages {
slog.Info("Creating custom page...", "title", cp.Title, "uri", cp.URI)
g.NewTemplate("custom_page",
cp.URI,
cp.Title,
cp.Image,
cp.Description,
cp).
Handle()
}
g.NotFoundHandler = func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}

View file

@ -236,3 +236,22 @@ h4 {
padding: 0 2rem 1rem 2rem;
border-radius: 32px;
}
.custom-page {
& h2 {
margin-bottom: 1rem;
}
& h3 {
margin-bottom: 0.5rem;
}
& ul {
list-style: disc inside;
}
& .links {
margin-top: 1rem;
margin-bottom: 1rem;
&:last-child {
margin-bottom: 0;
}
}
}

View file

@ -0,0 +1,11 @@
{{define "body"}}
<main class="custom-page" style="{{ .GetBackground }}">
<div class="header">
<h2>{{ .Title }}</h2>
<nav>
<a href="/">Home</a>
</nav>
</div>
{{ .GetContent }}
</main>
{{end}}