diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2025-12-13 17:15:47 +0000 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2025-12-13 17:15:47 +0000 |
| commit | 62fa3f77e8215fdaaf72ddb9df4162e0d65148da (patch) | |
| tree | dbf20dca933996cf253fe2d110d8381b0c407b2d /dom | |
| parent | ae297bbd117835304b298e7d8a2f914111940e77 (diff) | |
| parent | 48311424ba2eaac254864c008b6d18e8510f827d (diff) | |
Merge pull request '[Refactor] Replace manual DOM manipulation in markdown by cleaner one' (#2) from refactor/mardown-dom into main
Reviewed-on: https://git.anhgelus.world/anhgelus/small-web/pulls/2
Diffstat (limited to 'dom')
| -rw-r--r-- | dom/css_class.go | 45 | ||||
| -rw-r--r-- | dom/html.go | 119 |
2 files changed, 164 insertions, 0 deletions
diff --git a/dom/css_class.go b/dom/css_class.go new file mode 100644 index 0000000..998dd50 --- /dev/null +++ b/dom/css_class.go @@ -0,0 +1,45 @@ +package dom + +import "strings" + +type ClassList map[string]struct{} + +func (cl ClassList) set(e Element) Element { + if len(cl) == 0 { + return e + } + classes := "" + for k := range cl { + classes += k + " " + } + classes = strings.TrimSpace(classes) + return e.SetAttribute("class", classes) +} + +func (cl ClassList) Has(v string) bool { + _, ok := cl[v] + return ok +} + +func (cl ClassList) Add(v string) ClassList { + cl[v] = struct{}{} + return cl +} + +func (cl ClassList) Remove(v string) ClassList { + delete(cl, v) + return cl +} + +func (cl ClassList) Toggle(v string) ClassList { + if cl.Has(v) { + cl.Remove(v) + } else { + cl.Add(v) + } + return cl +} + +func NewClassList() ClassList { + return ClassList(make(map[string]struct{})) +} diff --git a/dom/html.go b/dom/html.go new file mode 100644 index 0000000..a7dc434 --- /dev/null +++ b/dom/html.go @@ -0,0 +1,119 @@ +package dom + +import ( + "fmt" + "html/template" +) + +func render(tag string, attributes map[string]string, endSlash bool) template.HTML { + base := fmt.Sprintf(`<%s`, tag) + for k, v := range attributes { + base += fmt.Sprintf(` %s="%s"`, k, v) + } + if !endSlash { + return template.HTML(base + `>`) + } + return template.HTML(base + ` />`) +} + +type Element interface { + Render() template.HTML + HasAttribute(string) bool + SetAttribute(string, string) Element + RemoveAttribute(string) Element + ClassList() ClassList +} + +type LiteralElement template.HTML + +func (e LiteralElement) Render() template.HTML { + return template.HTML(e) +} + +func (LiteralElement) HasAttribute(string) bool { + return false +} + +func (e LiteralElement) SetAttribute(string, string) Element { + return e +} + +func (e LiteralElement) RemoveAttribute(string) Element { + return e +} + +func (e LiteralElement) ClassList() ClassList { + return nil +} + +func NewLiteralElement(s template.HTML) LiteralElement { + return LiteralElement(s) +} + +type VoidElement struct { + Tag string + attributes map[string]string + cl ClassList +} + +func (e VoidElement) Render() template.HTML { + e.cl.set(e) + return render(e.Tag, e.attributes, true) +} + +func (e VoidElement) HasAttribute(k string) bool { + _, ok := e.attributes[k] + return ok +} + +func (e VoidElement) SetAttribute(k, v string) Element { + e.attributes[k] = v + return e +} + +func (e VoidElement) RemoveAttribute(k string) Element { + delete(e.attributes, k) + return e +} + +func (e VoidElement) ClassList() ClassList { + return e.cl +} + +func NewVoidElement(tag string) VoidElement { + return VoidElement{tag, make(map[string]string), NewClassList()} +} + +func NewImg(src, alt string) Element { + return NewVoidElement("img").SetAttribute("alt", alt).SetAttribute("src", src) +} + +type ContentElement struct { + VoidElement + Contents []Element +} + +func (e ContentElement) Render() template.HTML { + e.cl.set(e) + base := render(e.Tag, e.attributes, false) + for _, el := range e.Contents { + base += el.Render() + } + return base + template.HTML(fmt.Sprintf(`</%s>`, e.VoidElement.Tag)) +} + +func NewContentElement(tag string, contents []Element) ContentElement { + return ContentElement{NewVoidElement(tag), contents} +} + +func NewLiteralContentElement(tag string, content template.HTML) Element { + return NewContentElement(tag, []Element{NewLiteralElement(content)}) +} + +func NewParagraph(content template.HTML) Element { + return NewLiteralContentElement("p", content) +} + +func NewHeading(level uint, content template.HTML) Element { + return NewLiteralContentElement(fmt.Sprintf("h%d", level), content) +} |
