~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


bfe4e3ac Peter John

3 years ago
improve implementation
Files changed (2) hide show
  1. gsx/gsx.go +47 -25
  2. gsx/gsx_test.go +108 -56
gsx/gsx.go CHANGED
@@ -151,6 +151,8 @@ func convert(ref string, i interface{}) interface{} {
151
151
  return iv
152
152
  case string:
153
153
  return iv
154
+ case []*Tag:
155
+ return iv
154
156
  default:
155
157
  return iv
156
158
  }
@@ -181,10 +183,18 @@ func getRefValue(c *Context, ref string) interface{} {
181
183
  }
182
184
  }
183
185
 
186
+ func removeBrackets(s string) string {
187
+ return strings.ReplaceAll(strings.ReplaceAll(s, "{", ""), "}", "")
188
+ }
189
+
190
+ func removeQuotes(s string) string {
191
+ return strings.ReplaceAll(s, `"`, "")
192
+ }
193
+
184
194
  func substituteString(c *Context, v string) string {
185
195
  found := refRegex.FindString(v)
186
196
  if found != "" {
187
- varValue := fmt.Sprintf("%v", getRefValue(c, found))
197
+ varValue := fmt.Sprintf("%v", getRefValue(c, removeBrackets(found)))
188
198
  return strings.ReplaceAll(v, found, varValue)
189
199
  }
190
200
  return v
@@ -199,8 +209,16 @@ func populate(c *Context, tags []*Tag) []*Tag {
199
209
 
200
210
  func populateTag(c *Context, tag *Tag) {
201
211
  if tag.Name == "" {
202
- if tag.Text.Ref != nil && *tag.Text.Ref != "children" {
212
+ if tag.Text.Ref != nil {
203
- *tag.Text.Ref = substituteString(c, *tag.Text.Ref)
213
+ value := getRefValue(c, *tag.Text.Ref)
214
+ children, ok := value.([]*Tag)
215
+ if ok {
216
+ tag.Name = "fragment"
217
+ tag.Children = children
218
+ } else {
219
+ sValue := fmt.Sprintf("%+v", value)
220
+ tag.Text.Str = &sValue
221
+ }
204
222
  }
205
223
  } else {
206
224
  for _, a := range tag.Attributes {
@@ -233,36 +251,40 @@ func populateTag(c *Context, tag *Tag) {
233
251
  // }
234
252
  }
235
253
  }
236
- } else if a.Value.Ref != nil {
254
+ } else if a.Value.Str != nil {
237
- if a.Key == "class" {
238
- // classes := []string{}
239
- // kvstrings := strings.Split(strings.TrimSpace(at.Val), ",")
240
- // for _, kv := range kvstrings {
241
- // kvarray := strings.Split(kv, ":")
255
+ if strings.Contains(*a.Value.Str, "{") {
242
- // k := strings.TrimSpace(kvarray[0])
243
- // v := strings.TrimSpace(kvarray[1])
244
- // varValue := getRefValue(c, v)
245
- // if varValue.(bool) {
246
- // classes = append(classes, k)
247
- // }
248
- // }
249
- // n.Attr[i] = html.Attribute{
250
- // Namespace: at.Namespace,
251
- // Key: at.Key,
252
- // Val: strings.Join(classes, " "),
253
- // }
254
- } else {
255
- subs := substituteString(c, *a.Value.Ref)
256
+ subs := substituteString(c, removeQuotes(*a.Value.Str))
256
257
  a.Value = &Literal{Str: &subs}
258
+ } else {
259
+ *a.Value.Str = removeQuotes(*a.Value.Str)
257
260
  }
261
+ } else if a.Value.Ref != nil {
262
+ subs := substituteString(c, *a.Value.Ref)
263
+ a.Value = &Literal{Str: &subs}
264
+ } else if a.Key == "class" && a.Value.KV != nil {
265
+ classes := []string{}
266
+ for _, a := range a.Value.KV {
267
+ varValue := getRefValue(c, a.Value)
268
+ if varValue.(bool) {
269
+ classes = append(classes, removeQuotes(a.Key))
270
+ }
271
+ }
272
+ result := strings.Join(classes, " ")
273
+ a.Value.Str = &result
258
274
  }
259
275
  }
260
276
  if comp, ok := compMap[tag.Name]; ok {
277
+ if tag.SelfClosing {
278
+ tag.SelfClosing = false
279
+ }
261
280
  compContext := c.Clone(tag.Name)
262
281
  nodes := comp.Render(compContext, tag)
282
+ populate(compContext, tag.Children)
263
- // TODO: check if tag as Children already and {children} in defined in component
283
+ compContext.Set("children", tag.Children)
264
284
  tag.Children = nodes
285
+ populate(compContext, tag.Children)
286
+ } else {
287
+ populate(c, tag.Children)
265
288
  }
266
- populate(c, tag.Children)
267
289
  }
268
290
  }
gsx/gsx_test.go CHANGED
@@ -15,7 +15,7 @@ type TodoData struct {
15
15
 
16
16
  func Todo(c *Context, todo *TodoData) []*Tag {
17
17
  return c.Render(`
18
- <li id="todo-{todo.ID}" class="{ completed: todo.Completed }">
18
+ <li id="todo-{todo.ID}" class={"completed": todo.Completed }>
19
19
  <div class="upper">
20
20
  <span>{todo.Text}</span>
21
21
  <span>{todo.Text}</span>
@@ -29,18 +29,18 @@ func Todo(c *Context, todo *TodoData) []*Tag {
29
29
  `)
30
30
  }
31
31
 
32
- func TodoList(ctx Context, todos []*TodoData) []*Tag {
32
+ func TodoList(c *Context, todos []*TodoData) []*Tag {
33
- return ctx.Render(`
33
+ return c.Render(`
34
34
  <ul id="todo-list" class="relative" x-for="todo in todos">
35
35
  <Todo />
36
36
  </ul>
37
37
  `)
38
38
  }
39
39
 
40
- func TodoCount(ctx Context, count int) []*Tag {
40
+ func TodoCount(c *Context, count int) []*Tag {
41
- return ctx.Render(`
41
+ return c.Render(`
42
42
  <span id="todo-count" class="todo-count" hx-swap-oob="true">
43
- <strong>{count}</strong> item left
43
+ <strong>{count}</strong> "item left"
44
44
  </span>
45
45
  `)
46
46
  }
@@ -54,42 +54,116 @@ func TestComponent(t *testing.T) {
54
54
  RegisterComponent(Todo, nil, "todo")
55
55
  RegisterFunc(WebsiteName)
56
56
  h := Context{
57
- data: map[string]interface{}{
57
+ data: M{
58
- "todo": &TodoData{ID: "4", Text: "My fourth todo", Completed: false},
58
+ "todo": &TodoData{ID: "4", Text: "My fourth todo", Completed: true},
59
59
  },
60
60
  }
61
- actual := renderString(h.Render(`
61
+ nodes := h.Render(`
62
62
  <Todo>
63
- <div class="todo-panel">
64
- <span>{todo.Text}</span>
63
+ <span>{todo.Text}</span>
65
- <span>{todo.Completed}</span>
64
+ <span>{todo.Completed}</span>
66
- </div>
67
65
  </Todo>
68
66
  <Todo />
69
- `))
67
+ `)
68
+ actual := renderString(nodes)
70
69
  expected := strings.TrimLeft(`
71
70
  <Todo>
72
- <li id="todo-{todo.ID}"class="{ completed: todo.Completed }">
71
+ <li id="todo-4" class="completed">
73
- <div class="upper">
72
+ <div class="upper">
74
- <span>
73
+ <span>
75
- {todo.Text}
74
+ My fourth todo
76
- </span>
75
+ </span>
77
- <span>
76
+ <span>
78
- {todo.Text}
77
+ My fourth todo
79
- </span>
78
+ </span>
80
- </div>
79
+ </div>
80
+ <span>
81
- {children}
81
+ My fourth todo
82
+ </span>
83
+ <span>
84
+ true
85
+ </span>
86
+
82
- <div class="bottom">
87
+ <div class="bottom">
83
- <span>
88
+ <span>
84
- {todo.Completed}
89
+ true
85
- </span>
90
+ </span>
86
- <span>
91
+ <span>
87
- {todo.Completed}
92
+ true
88
- </span>
93
+ </span>
89
- </div>
94
+ </div>
90
- </li>
95
+ </li>
96
+ </Todo>
97
+ <Todo>
98
+ <li id="todo-4" class="completed">
99
+ <div class="upper">
100
+ <span>
101
+ My fourth todo
102
+ </span>
103
+ <span>
104
+ My fourth todo
105
+ </span>
106
+ </div>
107
+
108
+ <div class="bottom">
109
+ <span>
110
+ true
111
+ </span>
112
+ <span>
113
+ true
114
+ </span>
115
+ </div>
116
+ </li>
91
117
  </Todo>
118
+ `, "\n")
119
+ r.Equal(expected, actual)
120
+ }
121
+
122
+ func TestMultipleComponent(t *testing.T) {
123
+ r := require.New(t)
124
+ RegisterComponent(Todo, nil, "todo")
125
+ RegisterComponent(TodoCount, nil, "count")
126
+ h := Context{
127
+ data: M{
128
+ "todo": &TodoData{ID: "4", Text: "My fourth todo", Completed: true},
129
+ "count": 10,
130
+ },
131
+ }
132
+ nodes := h.Render(`
92
- <Todo />
133
+ <Todo />
134
+ <TodoCount />
135
+ `)
136
+ actual := renderString(nodes)
137
+ expected := strings.TrimLeft(`
138
+ <Todo>
139
+ <li id="todo-4" class="completed">
140
+ <div class="upper">
141
+ <span>
142
+ My fourth todo
143
+ </span>
144
+ <span>
145
+ My fourth todo
146
+ </span>
147
+ </div>
148
+
149
+ <div class="bottom">
150
+ <span>
151
+ true
152
+ </span>
153
+ <span>
154
+ true
155
+ </span>
156
+ </div>
157
+ </li>
158
+ </Todo>
159
+ <TodoCount>
160
+ <span id="todo-count" class="todo-count" hx-swap-oob="true">
161
+ <strong>
162
+ 10
163
+ </strong>
164
+ item left
165
+ </span>
166
+ </TodoCount>
93
167
  `, "\n")
94
168
  r.Equal(expected, actual)
95
169
  }
@@ -197,25 +271,3 @@ func TestComponent(t *testing.T) {
197
271
  // `)
198
272
  // r.Equal(expected, actual)
199
273
  // }
200
-
201
- // func TestMultipleComonent(t *testing.T) {
202
- // r := require.New(t)
203
- // RegisterComponent(Todo, nil, "todo")
204
- // RegisterComponent(TodoCount, nil, "count")
205
- // h := Context{
206
- // data: map[string]interface{}{
207
- // "todo": &TodoData{
208
- // ID: "3",
209
- // Text: "My third todo",
210
- // Completed: false,
211
- // },
212
- // },
213
- // }
214
- // actual := h.Render(`
215
- // <Todo />
216
- // <TodoCount />
217
- // `).String()
218
- // expected := stripWhitespace(`
219
- // `)
220
- // r.Equal(expected, actual)
221
- // }