-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathelement.go
More file actions
174 lines (154 loc) · 3.76 KB
/
Copy pathelement.go
File metadata and controls
174 lines (154 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package dom
import (
"github.com/tinywasm/fmt"
)
// Element represents a DOM element in the fluent Element API.
type Element struct {
tag string
id string
classes []string
attrs []fmt.KeyValue
events []eventHandler
children []any
void bool
}
// NewElement creates an Element with the given HTML tag.
// Used by tinywasm/html, tinywasm/svg, tinywasm/image to build elements.
func NewElement(tag string) *Element { return &Element{tag: tag} }
// NoCloseTag marks the element as self-closing (no closing tag rendered).
// Use for void HTML elements: br, hr, img, input, link, meta, etc.
func (b *Element) NoCloseTag() *Element {
b.void = true
return b
}
// ID sets the ID of the element.
func (b *Element) ID(id string) *Element {
b.id = id
return b
}
// For sets the for= attribute pointing to other's ID, auto-generating
// other's ID if it has none. Use for label/input pairing and aria-* references.
func (b *Element) For(other *Element) *Element {
if other == nil {
return b
}
return b.Attr("for", other.GetID())
}
// Class adds a class to the element.
func (b *Element) Class(class ...string) *Element {
b.classes = append(b.classes, class...)
return b
}
// Attr sets an attribute on the element.
func (b *Element) Attr(key, val string) *Element {
for i, attr := range b.attrs {
if attr.Key == key {
b.attrs[i].Value = val
return b
}
}
b.attrs = append(b.attrs, fmt.KeyValue{Key: key, Value: val})
return b
}
// On adds a generic event handler.
func (b *Element) On(t string, h func(Event)) *Element {
b.events = append(b.events, eventHandler{Name: t, Handler: h})
return b
}
// Add adds one or more children or attributes to the element.
// Children can be *Element, Component, string, or fmt.KeyValue.
func (b *Element) Add(children ...any) *Element {
for _, child := range children {
if attr, ok := child.(fmt.KeyValue); ok {
switch attr.Key {
case "class":
b.Class(attr.Value)
case "id":
b.ID(attr.Value)
default:
b.Attr(attr.Key, attr.Value)
}
continue
}
b.children = append(b.children, child)
}
return b
}
// Text adds a text node child.
func (b *Element) Text(text string) *Element {
b.children = append(b.children, text)
return b
}
// Render renders the element to the parent.
// This is a terminal operation.
func (b *Element) Render(parentID string) error {
return Render(parentID, b)
}
// Update triggers a re-render of the component.
func (b *Element) Update() {
Update(b)
}
// --- Component Interface Implementation ---
// GetID returns the element's ID.
func (b *Element) GetID() string {
if b.id == "" {
b.id = generateID()
}
return b.id
}
// SetID sets the element's ID.
func (b *Element) SetID(id string) {
b.id = id
}
// String serializes the element tree to its string representation.
func (b *Element) String() string {
return elementToHTML(b)
}
// Children returns the component's children (components only).
func (b *Element) Children() []Component {
var comps []Component
for _, child := range b.children {
if c, ok := child.(Component); ok {
comps = append(comps, c)
}
}
return comps
}
// Helper to convert Element to HTML string (recursive)
func elementToHTML(el *Element) string {
s := "<" + el.tag
if el.id != "" {
s += " id='" + el.id + "'"
}
if len(el.classes) > 0 {
s += " class='"
for i, c := range el.classes {
if i > 0 {
s += " "
}
s += c
}
s += "'"
}
for _, attr := range el.attrs {
s += " " + attr.Key + "='" + attr.Value + "'"
}
s += ">"
if el.void {
return s // No children, no closing tag
}
for _, child := range el.children {
switch v := child.(type) {
case *Element:
s += elementToHTML(v)
case string:
s += v
case Component:
s += v.String()
default:
s += fmt.Sprint(v)
}
}
s += "</" + el.tag + ">"
return s
}