diff --git a/README.md b/README.md
index a0d94fe..569305f 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ With `elem`, you can easily generate lists of elements from slices of data using
```go
items := []string{"Item 1", "Item 2", "Item 3"}
-liElements := elem.TransformEach(items, func(item string) *Element {
+liElements := elem.TransformEach(items, func(item string) Node {
return elem.Li(nil, elem.Text(item))
})
diff --git a/elem.go b/elem.go
index b42249b..8ad2471 100644
--- a/elem.go
+++ b/elem.go
@@ -29,10 +29,25 @@ var voidElements = map[string]struct{}{
type Attrs map[string]string
+type Node interface {
+ RenderTo(builder *strings.Builder)
+ Render() string
+}
+
+type TextNode string
+
+func (t TextNode) RenderTo(builder *strings.Builder) {
+ builder.WriteString(string(t))
+}
+
+func (t TextNode) Render() string {
+ return string(t)
+}
+
type Element struct {
Tag string
Attrs Attrs
- Children []interface{} // Can be either string (for text) or another Element
+ Children []Node
}
func (e *Element) RenderTo(builder *strings.Builder) {
@@ -65,14 +80,9 @@ func (e *Element) RenderTo(builder *strings.Builder) {
// Close opening tag
builder.WriteString(`>`)
- // Build the content (either child text or nested elements)
+ // Build the content
for _, child := range e.Children {
- switch c := child.(type) {
- case string:
- builder.WriteString(c)
- case *Element:
- c.RenderTo(builder)
- }
+ child.RenderTo(builder)
}
// Append closing tag
@@ -87,7 +97,7 @@ func (e *Element) Render() string {
return builder.String()
}
-func NewElement(tag string, attrs Attrs, children ...interface{}) *Element {
+func NewElement(tag string, attrs Attrs, children ...Node) *Element {
return &Element{
Tag: tag,
Attrs: attrs,
diff --git a/elements.go b/elements.go
index e36f220..9af90ad 100644
--- a/elements.go
+++ b/elements.go
@@ -2,25 +2,25 @@ package elem
// ========== Document Structure ==========
-func Body(props Attrs, children ...interface{}) *Element {
+func Body(props Attrs, children ...Node) *Element {
return NewElement("body", props, children...)
}
-func Head(props Attrs, children ...interface{}) *Element {
+func Head(props Attrs, children ...Node) *Element {
return NewElement("head", props, children...)
}
-func Html(props Attrs, children ...interface{}) *Element {
+func Html(props Attrs, children ...Node) *Element {
return NewElement("html", props, children...)
}
-func Title(props Attrs, children ...interface{}) *Element {
+func Title(props Attrs, children ...Node) *Element {
return NewElement("title", props, children...)
}
// ========== Text Formatting and Structure ==========
-func A(props Attrs, children ...interface{}) *Element {
+func A(props Attrs, children ...Node) *Element {
return NewElement("a", props, children...)
}
@@ -28,31 +28,31 @@ func Br(props Attrs) *Element {
return NewElement("br", props)
}
-func Blockquote(props Attrs, children ...interface{}) *Element {
+func Blockquote(props Attrs, children ...Node) *Element {
return NewElement("blockquote", props, children...)
}
-func Code(props Attrs, children ...interface{}) *Element {
+func Code(props Attrs, children ...Node) *Element {
return NewElement("code", props, children...)
}
-func Div(props Attrs, children ...interface{}) *Element {
+func Div(props Attrs, children ...Node) *Element {
return NewElement("div", props, children...)
}
-func Em(props Attrs, children ...interface{}) *Element {
+func Em(props Attrs, children ...Node) *Element {
return NewElement("em", props, children...)
}
-func H1(props Attrs, children ...interface{}) *Element {
+func H1(props Attrs, children ...Node) *Element {
return NewElement("h1", props, children...)
}
-func H2(props Attrs, children ...interface{}) *Element {
+func H2(props Attrs, children ...Node) *Element {
return NewElement("h2", props, children...)
}
-func H3(props Attrs, children ...interface{}) *Element {
+func H3(props Attrs, children ...Node) *Element {
return NewElement("h3", props, children...)
}
@@ -60,43 +60,43 @@ func Hr(props Attrs) *Element {
return NewElement("hr", props)
}
-func P(props Attrs, children ...interface{}) *Element {
+func P(props Attrs, children ...Node) *Element {
return NewElement("p", props, children...)
}
-func Pre(props Attrs, children ...interface{}) *Element {
+func Pre(props Attrs, children ...Node) *Element {
return NewElement("pre", props, children...)
}
-func Span(props Attrs, children ...interface{}) *Element {
+func Span(props Attrs, children ...Node) *Element {
return NewElement("span", props, children...)
}
-func Strong(props Attrs, children ...interface{}) *Element {
+func Strong(props Attrs, children ...Node) *Element {
return NewElement("strong", props, children...)
}
-func Text(content string) string {
- return content
+func Text(content string) TextNode {
+ return TextNode(content)
}
// ========== Lists ==========
-func Li(props Attrs, children ...interface{}) *Element {
+func Li(props Attrs, children ...Node) *Element {
return NewElement("li", props, children...)
}
-func Ul(props Attrs, children ...interface{}) *Element {
+func Ul(props Attrs, children ...Node) *Element {
return NewElement("ul", props, children...)
}
// ========== Forms ==========
-func Button(props Attrs, children ...interface{}) *Element {
+func Button(props Attrs, children ...Node) *Element {
return NewElement("button", props, children...)
}
-func Form(attrs Attrs, children ...interface{}) *Element {
+func Form(attrs Attrs, children ...Node) *Element {
return NewElement("form", attrs, children...)
}
@@ -104,19 +104,19 @@ func Input(attrs Attrs) *Element {
return NewElement("input", attrs)
}
-func Label(attrs Attrs, children ...interface{}) *Element {
+func Label(attrs Attrs, children ...Node) *Element {
return NewElement("label", attrs, children...)
}
-func Option(attrs Attrs, content string) *Element {
+func Option(attrs Attrs, content TextNode) *Element {
return NewElement("option", attrs, content)
}
-func Select(attrs Attrs, children ...interface{}) *Element {
+func Select(attrs Attrs, children ...Node) *Element {
return NewElement("select", attrs, children...)
}
-func Textarea(attrs Attrs, content string) *Element {
+func Textarea(attrs Attrs, content TextNode) *Element {
return NewElement("textarea", attrs, content)
}
@@ -132,6 +132,6 @@ func Meta(props Attrs) *Element {
return NewElement("meta", props)
}
-func Script(props Attrs, children ...interface{}) *Element {
+func Script(props Attrs, children ...Node) *Element {
return NewElement("script", props, children...)
}
diff --git a/elements_test.go b/elements_test.go
index 130243f..c9e39aa 100644
--- a/elements_test.go
+++ b/elements_test.go
@@ -152,19 +152,19 @@ func TestInput(t *testing.T) {
func TestLabel(t *testing.T) {
expected := ``
- el := Label(Attrs{attrs.For: "username"}, "Username")
+ el := Label(Attrs{attrs.For: "username"}, Text("Username"))
assert.Equal(t, expected, el.Render())
}
func TestSelectAndOption(t *testing.T) {
expected := ``
- el := Select(Attrs{attrs.Name: "color"}, Option(Attrs{attrs.Value: "red"}, "Red"), Option(Attrs{attrs.Value: "blue"}, "Blue"))
+ el := Select(Attrs{attrs.Name: "color"}, Option(Attrs{attrs.Value: "red"}, Text("Red")), Option(Attrs{attrs.Value: "blue"}, Text("Blue")))
assert.Equal(t, expected, el.Render())
}
func TestTextarea(t *testing.T) {
expected := ``
- el := Textarea(Attrs{attrs.Name: "comment", attrs.Rows: "5"}, "Leave a comment...")
+ el := Textarea(Attrs{attrs.Name: "comment", attrs.Rows: "5"}, Text("Leave a comment..."))
assert.Equal(t, expected, el.Render())
}
diff --git a/examples/htmx-fiber-form/main.go b/examples/htmx-fiber-form/main.go
index d1a36ae..2a8da0c 100644
--- a/examples/htmx-fiber-form/main.go
+++ b/examples/htmx-fiber-form/main.go
@@ -42,14 +42,14 @@ func main() {
htmx.HXPost: "/submit-form",
htmx.HXSwap: "outerHTML",
},
- elem.Label(elem.Attrs{attrs.For: "name"}, "Name: "),
+ elem.Label(elem.Attrs{attrs.For: "name"}, elem.Text("Name: ")),
elem.Input(elem.Attrs{
attrs.Type: "text",
attrs.Name: "name",
attrs.ID: "name",
}),
elem.Br(nil),
- elem.Label(elem.Attrs{attrs.For: "email"}, "Email: "),
+ elem.Label(elem.Attrs{attrs.For: "email"}, elem.Text("Email: ")),
elem.Input(elem.Attrs{
attrs.Type: "email",
attrs.Name: "email",
diff --git a/utils.go b/utils.go
index 36f84fd..d60cb80 100644
--- a/utils.go
+++ b/utils.go
@@ -39,10 +39,10 @@ func Show(condition bool, ifTrue, ifFalse *Element) *Element {
}
// TransformEach maps a slice of items to a slice of Elements using the provided function
-func TransformEach[T any](items []T, fn func(T) *Element) []*Element {
- var elements []*Element
+func TransformEach[T any](items []T, fn func(T) Node) []Node {
+ var nodes []Node
for _, item := range items {
- elements = append(elements, fn(item))
+ nodes = append(nodes, fn(item))
}
- return elements
+ return nodes
}
diff --git a/utils_test.go b/utils_test.go
index 6573af3..ff7a2f5 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -43,7 +43,7 @@ func TestShow(t *testing.T) {
func TestTransformEach(t *testing.T) {
items := []string{"Item 1", "Item 2", "Item 3"}
- elements := TransformEach(items, func(item string) *Element {
+ elements := TransformEach(items, func(item string) Node {
return Li(nil, Text(item))
})