-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
30e9d66
commit 89d9c8f
Showing
6 changed files
with
270 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package html | ||
|
||
import ( | ||
"fmt" | ||
"slices" | ||
"strings" | ||
) | ||
|
||
var _ HTMLer = new(Element) | ||
|
||
type Element struct { | ||
tag string | ||
attrs map[string]string | ||
content HTML | ||
} | ||
|
||
func (e *Element) Attribute(name, value string) *Element { | ||
e.attrs[name] = value | ||
return e | ||
} | ||
|
||
func (e *Element) Class(class ...string) *Element { | ||
return e.Attribute("class", strings.Join(class, " ")) | ||
} | ||
|
||
func (e *Element) Href(href string) *Element { | ||
return e.Attribute("href", href) | ||
} | ||
|
||
func (e *Element) Src(src string) *Element { | ||
return e.Attribute("src", src) | ||
} | ||
|
||
func (e *Element) Style(style string) *Element { | ||
return e.Attribute("style", style) | ||
} | ||
|
||
func content(v any) HTML { | ||
switch v := v.(type) { | ||
case nil: | ||
return "" | ||
case HTML: | ||
return v | ||
case HTMLer: | ||
return v.HTML() | ||
case string: | ||
return HTML(EscapeString(v)) | ||
default: | ||
return HTML(EscapeString(fmt.Sprint(v))) | ||
} | ||
} | ||
|
||
func (e *Element) Content(v any) *Element { | ||
e.content = content(v) | ||
return e | ||
} | ||
|
||
func (e *Element) AppendContent(v any) *Element { | ||
e.content += content(v) | ||
return e | ||
} | ||
|
||
func (e *Element) AppendChild(child *Element) *Element { | ||
return e.AppendContent(child) | ||
} | ||
|
||
// https://developer.mozilla.org/en-US/docs/Glossary/Void_element | ||
func (e Element) isVoidElement() bool { | ||
return slices.Contains([]string{ | ||
"area", | ||
"base", | ||
"br", | ||
"col", | ||
"embed", | ||
"hr", | ||
"img", | ||
"input", | ||
"link", | ||
"meta", | ||
"param", | ||
"source", | ||
"track", | ||
"wbr", | ||
}, strings.ToLower(e.tag)) | ||
} | ||
|
||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes | ||
func (e Element) printAttrs() string { | ||
var s []string | ||
for k, v := range e.attrs { | ||
if v == "" || v == "true" { | ||
s = append(s, k) | ||
} else if v == "false" { | ||
continue | ||
} else { | ||
s = append(s, fmt.Sprintf("%s=%q", k, v)) | ||
} | ||
} | ||
slices.Sort(s) | ||
return strings.Join(s, " ") | ||
} | ||
|
||
func (e *Element) HTML() HTML { | ||
var b strings.Builder | ||
fmt.Fprint(&b, "<", e.tag) | ||
if attrs := e.printAttrs(); attrs != "" { | ||
fmt.Fprint(&b, " ", attrs) | ||
} | ||
if e.isVoidElement() { | ||
fmt.Fprint(&b, ">") | ||
} else { | ||
fmt.Fprint(&b, ">", e.content) | ||
fmt.Fprintf(&b, "</%s>", e.tag) | ||
} | ||
return HTML(b.String()) | ||
} | ||
|
||
func NewElement(tag string) *Element { | ||
return &Element{tag, make(map[string]string), ""} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package html | ||
|
||
import "testing" | ||
|
||
func TestElement(t *testing.T) { | ||
for _, tc := range []struct { | ||
tag string | ||
attrs [][2]string | ||
content string | ||
html HTML | ||
}{ | ||
{"a", [][2]string{{"href", "/"}, {"style", "display:none"}}, "test", `<a href="/" style="display:none">test</a>`}, | ||
{"p", nil, "test", "<p>test</p>"}, | ||
{"p", [][2]string{{"hidden", "true"}}, "test", "<p hidden>test</p>"}, | ||
{"p", [][2]string{{"hidden", "false"}}, "test", "<p>test</p>"}, | ||
{"div", nil, "<test>", "<div><test></div>"}, | ||
} { | ||
e := NewElement(tc.tag).Content(tc.content) | ||
for _, i := range tc.attrs { | ||
e.Attribute(i[0], i[1]) | ||
} | ||
if res := e.HTML(); tc.html != res { | ||
t.Errorf("expected %q; got %q", tc.html, res) | ||
} | ||
} | ||
if div, expect := Div().Content(Br()).HTML(), "<div><br></div>"; expect != string(div) { | ||
t.Errorf("expected %q; got %q", expect, div) | ||
} | ||
} | ||
|
||
func TestAppend(t *testing.T) { | ||
e := Div() | ||
if expect := "<div></div>"; expect != string(e.HTML()) { | ||
t.Errorf("expected %q; got %q", expect, e.HTML()) | ||
} | ||
e.AppendContent("test") | ||
if expect := "<div>test</div>"; expect != string(e.HTML()) { | ||
t.Errorf("expected %q; got %q", expect, e.HTML()) | ||
} | ||
e.AppendChild(Img().Src("test")) | ||
if expect := `<div>test<img src="test"></div>`; expect != string(e.HTML()) { | ||
t.Errorf("expected %q; got %q", expect, e.HTML()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,36 @@ | ||
package html | ||
|
||
import ( | ||
"fmt" | ||
"html" | ||
"strings" | ||
) | ||
import "html" | ||
|
||
var ( | ||
EscapeString = html.EscapeString | ||
UnescapeString = html.UnescapeString | ||
) | ||
|
||
type Attribute struct { | ||
Name string | ||
Value string | ||
} | ||
|
||
func Attributes(pairs ...string) (attributes []Attribute) { | ||
if len(pairs)%2 != 0 { | ||
panic("pairs must have even number of elements") | ||
} | ||
for i := 0; i < len(pairs); i = i + 2 { | ||
attributes = append(attributes, Attribute{pairs[i], pairs[i+1]}) | ||
} | ||
return | ||
} | ||
|
||
func (attr Attribute) String() string { | ||
if attr.Value == "" || attr.Value == "true" { | ||
return attr.Name | ||
} | ||
return fmt.Sprintf("%s=%q", attr.Name, attr.Value) | ||
} | ||
|
||
type HTML string | ||
|
||
func Element[T HTML | string](tag string, attributes []Attribute, content T) HTML { | ||
var b strings.Builder | ||
fmt.Fprint(&b, "<", tag) | ||
for _, i := range attributes { | ||
fmt.Fprint(&b, " ", i) | ||
} | ||
fmt.Fprint(&b, ">") | ||
switch any(content).(type) { | ||
case HTML: | ||
fmt.Fprint(&b, content) | ||
default: | ||
fmt.Fprint(&b, EscapeString(string(content))) | ||
} | ||
fmt.Fprintf(&b, "</%s>", tag) | ||
return HTML(b.String()) | ||
type HTMLer interface { | ||
HTML() HTML | ||
} | ||
|
||
func A() *Element { return NewElement("a") } | ||
func B() *Element { return NewElement("b") } | ||
func Br() *Element { return NewElement("br") } | ||
func Div() *Element { return NewElement("div") } | ||
func Em() *Element { return NewElement("em") } | ||
func Form() *Element { return NewElement("form") } | ||
func H1() *Element { return NewElement("h1") } | ||
func H2() *Element { return NewElement("h2") } | ||
func I() *Element { return NewElement("i") } | ||
func Img() *Element { return NewElement("img") } | ||
func Input() *Element { return NewElement("input") } | ||
func Label() *Element { return NewElement("label") } | ||
func Li() *Element { return NewElement("li") } | ||
func P() *Element { return NewElement("p") } | ||
func Span() *Element { return NewElement("span") } | ||
func Svg() *Element { return NewElement("svg") } | ||
func Table() *Element { return NewElement("table") } | ||
func Tbody() *Element { return NewElement("tbody") } | ||
func Title() *Element { return NewElement("title") } | ||
func Thead() *Element { return NewElement("thead") } | ||
func Ul() *Element { return NewElement("ul") } |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package html | ||
|
||
import "strconv" | ||
|
||
func Tr[T *TableHeader | *TableData](element ...T) *Element { | ||
tr := NewElement("tr") | ||
for _, i := range element { | ||
tr.AppendContent(i) | ||
} | ||
return tr | ||
} | ||
|
||
var ( | ||
_ HTMLer = new(TableHeader) | ||
_ HTMLer = new(TableData) | ||
) | ||
|
||
type ( | ||
TableHeader struct{ *Element } | ||
TableData struct{ *Element } | ||
) | ||
|
||
func Th(content any) *TableHeader { | ||
return &TableHeader{NewElement("th").Content(content)} | ||
} | ||
|
||
func (th *TableHeader) Abbr(abbr string) *TableHeader { | ||
th.Element.Attribute("abbr", abbr) | ||
return th | ||
} | ||
|
||
func (th *TableHeader) Colspan(n uint) *TableHeader { | ||
th.Element.Attribute("colspan", strconv.FormatUint(uint64(n), 10)) | ||
return th | ||
} | ||
|
||
func (th *TableHeader) Headers(headers string) *TableHeader { | ||
th.Element.Attribute("headers", headers) | ||
return th | ||
} | ||
|
||
func (th *TableHeader) Rowspan(n uint) *TableHeader { | ||
th.Element.Attribute("rowspan", strconv.FormatUint(uint64(n), 10)) | ||
return th | ||
} | ||
|
||
func (th *TableHeader) Scope(scope string) *TableHeader { | ||
th.Element.Attribute("scope", scope) | ||
return th | ||
} | ||
|
||
func Td(content any) *TableData { | ||
return &TableData{NewElement("td").Content(content)} | ||
} | ||
|
||
func (td *TableData) Colspan(n uint) *TableData { | ||
td.Element.Attribute("colspan", strconv.FormatUint(uint64(n), 10)) | ||
return td | ||
} | ||
|
||
func (td *TableData) Headers(headers string) *TableData { | ||
td.Element.Attribute("headers", headers) | ||
return td | ||
} | ||
|
||
func (td *TableData) Rowspan(n uint) *TableData { | ||
td.Element.Attribute("rowspan", strconv.FormatUint(uint64(n), 10)) | ||
return td | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package html | ||
|
||
import "testing" | ||
|
||
func TestTable(t *testing.T) { | ||
table := `<table><thead><tr><th colspan="2">H1</th><th>H2</th></tr></thead><tbody><tr><td>B1</td><td>B2</td></tr></tbody></table>` | ||
if e := Table(). | ||
AppendChild(Thead().AppendChild(Tr(Th("H1").Colspan(2), Th("H2")))). | ||
AppendChild(Tbody().AppendChild(Tr(Td("B1"), Td("B2")))); string(e.HTML()) != table { | ||
t.Errorf("expected %q; got %q", table, e.HTML()) | ||
} | ||
} |