From 16a073a12372eb13fb86618be684b6fd19902828 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sat, 13 Dec 2025 16:58:20 +0100 Subject: feat(dom): represents every html element --- dom/html.go | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 dom/html.go (limited to 'dom') diff --git a/dom/html.go b/dom/html.go new file mode 100644 index 0000000..f43526c --- /dev/null +++ b/dom/html.go @@ -0,0 +1,100 @@ +package dom + +import ( + "fmt" + "html" + "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 +} + +type LiteralElement struct { + Content string +} + +func (e LiteralElement) Render() template.HTML { + return template.HTML(html.EscapeString(e.Content)) +} + +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 NewLiteralElement(s string) LiteralElement { + return LiteralElement{s} +} + +type VoidElement struct { + Tag string + attributes map[string]string +} + +func (e VoidElement) Render() template.HTML { + 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 NewVoidElement(tag string) VoidElement { + return VoidElement{tag, make(map[string]string)} +} + +func NewImg(src, alt string) Element { + return NewVoidElement("img").SetAttribute("src", src).SetAttribute("alt", alt) +} + +type ContentElement struct { + VoidElement + Content Element +} + +func (e ContentElement) Render() template.HTML { + base := render(e.Tag, e.attributes, false) + base += e.Content.Render() + return base + template.HTML(fmt.Sprintf(``, e.VoidElement.Tag)) +} + +func NewContentElement(tag string, content Element) ContentElement { + return ContentElement{NewVoidElement(tag), content} +} + +func NewParagraph(content string) Element { + return NewContentElement("p", NewLiteralElement(content)) +} -- cgit v1.2.3 From d695b39edf898e056a8de3724a020f15754fcea9 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sat, 13 Dec 2025 17:17:14 +0100 Subject: feat(dom): type to represents css class --- dom/css_class.go | 43 +++++++++++++++++++++++++++++++++++++++++++ dom/html.go | 26 ++++++++++++++++++++------ 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 dom/css_class.go (limited to 'dom') diff --git a/dom/css_class.go b/dom/css_class.go new file mode 100644 index 0000000..633869e --- /dev/null +++ b/dom/css_class.go @@ -0,0 +1,43 @@ +package dom + +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 = classes[:len(cl)-1] + 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 index f43526c..360a33b 100644 --- a/dom/html.go +++ b/dom/html.go @@ -22,6 +22,7 @@ type Element interface { HasAttribute(string) bool SetAttribute(string, string) Element RemoveAttribute(string) Element + ClassList() ClassList } type LiteralElement struct { @@ -44,6 +45,10 @@ func (e LiteralElement) RemoveAttribute(string) Element { return e } +func (e LiteralElement) ClassList() ClassList { + return nil +} + func NewLiteralElement(s string) LiteralElement { return LiteralElement{s} } @@ -51,9 +56,11 @@ func NewLiteralElement(s string) LiteralElement { type VoidElement struct { Tag string attributes map[string]string + cl ClassList } func (e VoidElement) Render() template.HTML { + e = e.cl.set(e).(VoidElement) return render(e.Tag, e.attributes, true) } @@ -72,8 +79,12 @@ func (e VoidElement) RemoveAttribute(k string) Element { return e } +func (e VoidElement) ClassList() ClassList { + return e.cl +} + func NewVoidElement(tag string) VoidElement { - return VoidElement{tag, make(map[string]string)} + return VoidElement{tag, make(map[string]string), NewClassList()} } func NewImg(src, alt string) Element { @@ -82,19 +93,22 @@ func NewImg(src, alt string) Element { type ContentElement struct { VoidElement - Content Element + Contents []Element } func (e ContentElement) Render() template.HTML { + e = e.cl.set(e).(ContentElement) base := render(e.Tag, e.attributes, false) - base += e.Content.Render() + for _, el := range e.Contents { + base += el.Render() + } return base + template.HTML(fmt.Sprintf(``, e.VoidElement.Tag)) } -func NewContentElement(tag string, content Element) ContentElement { - return ContentElement{NewVoidElement(tag), content} +func NewContentElement(tag string, contents []Element) ContentElement { + return ContentElement{NewVoidElement(tag), contents} } func NewParagraph(content string) Element { - return NewContentElement("p", NewLiteralElement(content)) + return NewContentElement("p", []Element{NewLiteralElement(content)}) } -- cgit v1.2.3 From aea07c141b3a2448ea367ad80ad5e12d04a78df7 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sat, 13 Dec 2025 18:09:40 +0100 Subject: refactor(markdown): use new dom package to create html --- dom/css_class.go | 4 +++- dom/html.go | 29 +++++++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'dom') diff --git a/dom/css_class.go b/dom/css_class.go index 633869e..998dd50 100644 --- a/dom/css_class.go +++ b/dom/css_class.go @@ -1,5 +1,7 @@ package dom +import "strings" + type ClassList map[string]struct{} func (cl ClassList) set(e Element) Element { @@ -10,7 +12,7 @@ func (cl ClassList) set(e Element) Element { for k := range cl { classes += k + " " } - classes = classes[:len(cl)-1] + classes = strings.TrimSpace(classes) return e.SetAttribute("class", classes) } diff --git a/dom/html.go b/dom/html.go index 360a33b..a7dc434 100644 --- a/dom/html.go +++ b/dom/html.go @@ -2,7 +2,6 @@ package dom import ( "fmt" - "html" "html/template" ) @@ -25,12 +24,10 @@ type Element interface { ClassList() ClassList } -type LiteralElement struct { - Content string -} +type LiteralElement template.HTML func (e LiteralElement) Render() template.HTML { - return template.HTML(html.EscapeString(e.Content)) + return template.HTML(e) } func (LiteralElement) HasAttribute(string) bool { @@ -49,8 +46,8 @@ func (e LiteralElement) ClassList() ClassList { return nil } -func NewLiteralElement(s string) LiteralElement { - return LiteralElement{s} +func NewLiteralElement(s template.HTML) LiteralElement { + return LiteralElement(s) } type VoidElement struct { @@ -60,7 +57,7 @@ type VoidElement struct { } func (e VoidElement) Render() template.HTML { - e = e.cl.set(e).(VoidElement) + e.cl.set(e) return render(e.Tag, e.attributes, true) } @@ -88,7 +85,7 @@ func NewVoidElement(tag string) VoidElement { } func NewImg(src, alt string) Element { - return NewVoidElement("img").SetAttribute("src", src).SetAttribute("alt", alt) + return NewVoidElement("img").SetAttribute("alt", alt).SetAttribute("src", src) } type ContentElement struct { @@ -97,7 +94,7 @@ type ContentElement struct { } func (e ContentElement) Render() template.HTML { - e = e.cl.set(e).(ContentElement) + e.cl.set(e) base := render(e.Tag, e.attributes, false) for _, el := range e.Contents { base += el.Render() @@ -109,6 +106,14 @@ func NewContentElement(tag string, contents []Element) ContentElement { return ContentElement{NewVoidElement(tag), contents} } -func NewParagraph(content string) Element { - return NewContentElement("p", []Element{NewLiteralElement(content)}) +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) } -- cgit v1.2.3