~repos /gromer
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.
a5c4f8b2
—
Peter John 4 years ago
add hooks
- .snapshots/{TestHtmlPage.html → TestHtml.html} +6 -6
- go.mod +1 -0
- go.sum +2 -0
- hooks.go +31 -0
- hooks_test.go +31 -0
- html.go +12 -8
- html_test.go +69 -70
.snapshots/{TestHtmlPage.html → TestHtml.html}
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
|
6
|
-
<meta
|
|
6
|
+
<meta content="utf-8" http-equiv="encoding">
|
|
7
7
|
<title> 123 </title>
|
|
8
8
|
|
|
9
9
|
<meta name="description" content="123">
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
<meta name="keywords" content="123">
|
|
12
12
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover">
|
|
13
13
|
<link rel="icon" href="/assets/icon.png">
|
|
14
|
-
<link
|
|
14
|
+
<link href="/assets/icon.png" rel="apple-touch-icon">
|
|
15
15
|
<link rel="stylesheet" href="/assets/styles.css">
|
|
16
16
|
<script src="/assets/alpine.js" defer></script>
|
|
17
17
|
|
|
18
|
-
<meta
|
|
18
|
+
<meta content="title" name="title">
|
|
19
19
|
<style>
|
|
20
20
|
*, ::before, ::after { box-sizing: border-box; }
|
|
21
21
|
html { -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; line-height: 1.15; -webkit-text-size-adjust: 100%; }
|
|
@@ -89,11 +89,11 @@
|
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
91
|
<div class="flex flex-row justify-center items-center">
|
|
92
|
-
<button @click="
|
|
92
|
+
<button @click="decrement"> - </button>
|
|
93
93
|
|
|
94
|
-
<div
|
|
94
|
+
<div class="flex flex-row justify-center items-center m-20 text-5xl" x-text="count"> 4 </div>
|
|
95
95
|
|
|
96
|
-
<button @click="
|
|
96
|
+
<button @click="increment"> + </button>
|
|
97
97
|
</div>
|
|
98
98
|
</div>
|
|
99
99
|
|
go.mod
CHANGED
|
@@ -7,6 +7,7 @@ require (
|
|
|
7
7
|
github.com/apex/gateway/v2 v2.0.0
|
|
8
8
|
github.com/aymerick/raymond v2.0.2+incompatible // indirect
|
|
9
9
|
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible // indirect
|
|
10
|
+
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf // indirect
|
|
10
11
|
github.com/gobuffalo/velvet v0.0.0-20170320144106-d97471bf5d8f // indirect
|
|
11
12
|
github.com/gorilla/mux v1.8.0
|
|
12
13
|
github.com/kr/pretty v0.1.0 // indirect
|
go.sum
CHANGED
|
@@ -18,6 +18,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
|
|
18
18
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
19
19
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
20
20
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
21
|
+
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf h1:NrF81UtW8gG2LBGkXFQFqlfNnvMt9WdB46sfdJY4oqc=
|
|
22
|
+
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
|
|
21
23
|
github.com/gobuffalo/envy v1.6.5 h1:X3is06x7v0nW2xiy2yFbbIjwHz57CD6z6MkvqULTCm8=
|
|
22
24
|
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
|
23
25
|
github.com/gobuffalo/velvet v0.0.0-20170320144106-d97471bf5d8f h1:ddIdPdlkAgKMB0mbkft2LT3oxN1h3MN1fopCFrOgkhY=
|
hooks.go
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package wapp
|
|
2
|
+
|
|
3
|
+
type Context struct {
|
|
4
|
+
index int
|
|
5
|
+
datas []interface{}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Go 1.18 stuff
|
|
9
|
+
// func UseState[T any](ctx *Context , s T) (func() T, func(v T)) {
|
|
10
|
+
// if len(ctx.datas) <= ctx.index + 1 {
|
|
11
|
+
// ctx .datas = append(ctx.datas, s)
|
|
12
|
+
// }
|
|
13
|
+
// return func() T {
|
|
14
|
+
// return ctx.datas[ctx.index].(T)
|
|
15
|
+
// }, func(v T) {
|
|
16
|
+
// ctx.datas[ctx.index] = v
|
|
17
|
+
// }
|
|
18
|
+
// }
|
|
19
|
+
|
|
20
|
+
func UseState(c *Context, s interface{}) (func() interface{}, func(v interface{})) {
|
|
21
|
+
localIndex := c.index
|
|
22
|
+
if len(c.datas) <= c.index+1 {
|
|
23
|
+
c.datas = append(c.datas, s)
|
|
24
|
+
c.index += 1
|
|
25
|
+
}
|
|
26
|
+
return func() interface{} {
|
|
27
|
+
return c.datas[localIndex].(interface{})
|
|
28
|
+
}, func(v interface{}) {
|
|
29
|
+
c.datas[localIndex] = v
|
|
30
|
+
}
|
|
31
|
+
}
|
hooks_test.go
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package wapp
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
. "github.com/franela/goblin"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func TestHooks(t *testing.T) {
|
|
10
|
+
g := Goblin(t)
|
|
11
|
+
g.Describe("useState", func() {
|
|
12
|
+
ctx := &Context{index: 0, datas: []interface{}{}}
|
|
13
|
+
getValue, setValue := UseState(ctx, 12)
|
|
14
|
+
|
|
15
|
+
g.It("should be initialized ", func() {
|
|
16
|
+
g.Assert(1).Equal(ctx.index)
|
|
17
|
+
g.Assert(1).Equal(len(ctx.datas))
|
|
18
|
+
g.Assert(ctx.datas[0]).Equal(12)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
g.It("should get value ", func() {
|
|
22
|
+
g.Assert(getValue()).Equal(12)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
g.It("should set value", func() {
|
|
26
|
+
setValue(15)
|
|
27
|
+
g.Assert(ctx.datas[0]).Equal(15)
|
|
28
|
+
g.Assert(getValue()).Equal(15)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
}
|
html.go
CHANGED
|
@@ -24,6 +24,16 @@ func mergeAttributes(parent *Element, uis ...interface{}) *Element {
|
|
|
24
24
|
switch c := v.(type) {
|
|
25
25
|
case Attribute:
|
|
26
26
|
parent.setAttr(c.Key, c.Value)
|
|
27
|
+
case M:
|
|
28
|
+
for k, v := range c {
|
|
29
|
+
if a, ok := v.(string); ok {
|
|
30
|
+
parent.setAttr(k, a)
|
|
31
|
+
} else {
|
|
32
|
+
// store some server state on the component ctx
|
|
33
|
+
// parent.setAttr(k, a)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
27
37
|
case *Element:
|
|
28
38
|
elems = append(elems, c)
|
|
29
39
|
case nil:
|
|
@@ -339,14 +349,8 @@ func Attr(k, v string) Attribute {
|
|
|
339
349
|
return Attribute{k, v}
|
|
340
350
|
}
|
|
341
351
|
|
|
342
|
-
func GetFunctionName(i interface{}) string {
|
|
343
|
-
fnName := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
|
344
|
-
parts := strings.Split(fnName, ".")
|
|
345
|
-
return strings.Replace(parts[len(parts)-1], "-fm", "", 1)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
func OnClick(v
|
|
352
|
+
func OnClick(v string) Attribute {
|
|
349
|
-
return Attribute{"@click",
|
|
353
|
+
return Attribute{"@click", v}
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
func ID(v string) Attribute {
|
html_test.go
CHANGED
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"testing"
|
|
7
7
|
|
|
8
8
|
"github.com/bradleyjkemp/cupaloy"
|
|
9
|
+
. "github.com/franela/goblin"
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
func Row(uis ...interface{}) *Element {
|
|
@@ -16,24 +17,14 @@ func Col(uis ...interface{}) *Element {
|
|
|
16
17
|
return NewElement("div", false, append([]interface{}{Css("flex flex-col justify-center items-center")}, uis...)...)
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
func Counter(c *Context, start int) *Element {
|
|
19
|
-
|
|
21
|
+
count, setCount := UseState(c, start)
|
|
22
|
+
increment := func() {
|
|
20
|
-
|
|
23
|
+
setCount(count().(int) + 1)
|
|
21
|
-
}
|
|
24
|
+
}
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
decrement := func() {
|
|
24
|
-
|
|
26
|
+
setCount(count().(int) + 1)
|
|
25
|
-
return c.Render()
|
|
26
|
-
}
|
|
27
|
+
}
|
|
27
|
-
|
|
28
|
-
func (c *CounterState) Increment() {
|
|
29
|
-
c.Count += 1
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
func (c *CounterState) Decrement() {
|
|
33
|
-
c.Count -= 1
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
func (c *CounterState) Render() *Element {
|
|
37
28
|
return Col(Css("text-3xl text-gray-700"),
|
|
38
29
|
Row(
|
|
39
30
|
Row(Css("underline"),
|
|
@@ -41,61 +32,69 @@ func (c *CounterState) Render() *Element {
|
|
|
41
32
|
),
|
|
42
33
|
),
|
|
43
34
|
Row(
|
|
44
|
-
Button(OnClick(
|
|
35
|
+
Button(OnClick("decrement"), Text("-")),
|
|
45
|
-
Row(Css("m-20 text-5xl"), XText("
|
|
36
|
+
Row(Css("m-20 text-5xl"), XText("count"),
|
|
46
|
-
Text(strconv.Itoa(
|
|
37
|
+
Text(strconv.Itoa(count().(int))),
|
|
47
38
|
),
|
|
48
|
-
Button(OnClick(
|
|
39
|
+
Button(OnClick("increment"), Text("+")),
|
|
49
40
|
),
|
|
41
|
+
M{
|
|
42
|
+
"count": count,
|
|
43
|
+
"increment": increment,
|
|
44
|
+
"decrement": decrement,
|
|
45
|
+
},
|
|
50
46
|
)
|
|
47
|
+
// return `
|
|
48
|
+
// <div class="flex flex-col justify-center items-center text-3xl text-gray-700">
|
|
49
|
+
// <div class="flex flex-row justify-center items-center">
|
|
50
|
+
// <div class="flex flex-row justify-center items-center underline">
|
|
51
|
+
// Counter
|
|
52
|
+
// </div>
|
|
53
|
+
// </div>
|
|
54
|
+
// <div class="flex flex-row justify-center items-center">
|
|
55
|
+
// <button class="btn m-20" @click="Increment">
|
|
56
|
+
// -
|
|
57
|
+
// </button>
|
|
58
|
+
// <div class="flex flex-row justify-center items-center m-20 text-8xl">
|
|
59
|
+
// {{ count }}
|
|
60
|
+
// </div>
|
|
61
|
+
// <button class="btn m-20" @click="Decrement">
|
|
62
|
+
// +
|
|
63
|
+
// </button>
|
|
64
|
+
// </div>
|
|
65
|
+
// </div>
|
|
66
|
+
// `
|
|
51
67
|
}
|
|
52
68
|
|
|
53
|
-
// func CounterHtml() string {
|
|
54
|
-
// return `
|
|
55
|
-
// <div class="flex flex-col justify-center items-center text-3xl text-gray-700">
|
|
56
|
-
// <div class="flex flex-row justify-center items-center">
|
|
57
|
-
// <div class="flex flex-row justify-center items-center underline">
|
|
58
|
-
// Counter
|
|
59
|
-
// </div>
|
|
60
|
-
// </div>
|
|
61
|
-
// <div class="flex flex-row justify-center items-center">
|
|
62
|
-
// <button class="btn m-20" @click={{actions.Increment}}>
|
|
63
|
-
// -
|
|
64
|
-
// </button>
|
|
65
|
-
// <div class="flex flex-row justify-center items-center m-20 text-8xl">
|
|
66
|
-
// {{ state.count }}
|
|
67
|
-
// </div>
|
|
68
|
-
// <button class="btn m-20" @click={{actions.Decrement}}>
|
|
69
|
-
// +
|
|
70
|
-
// </button>
|
|
71
|
-
// </div>
|
|
72
|
-
// </div>
|
|
73
|
-
// `
|
|
74
|
-
// }
|
|
75
|
-
|
|
76
|
-
func
|
|
69
|
+
func TestHtml(t *testing.T) {
|
|
70
|
+
g := Goblin(t)
|
|
71
|
+
g.Describe("Html", func() {
|
|
72
|
+
g.It("should match snapshot", func() {
|
|
73
|
+
ctx := &Context{index: 0, datas: []interface{}{}}
|
|
77
|
-
|
|
74
|
+
b := bytes.NewBuffer(nil)
|
|
78
|
-
|
|
75
|
+
p := Html(
|
|
79
|
-
|
|
76
|
+
Head(
|
|
80
|
-
|
|
77
|
+
Title("123"),
|
|
81
|
-
|
|
78
|
+
Meta("description", "123"),
|
|
82
|
-
|
|
79
|
+
Meta("author", "123"),
|
|
83
|
-
|
|
80
|
+
Meta("keywords", "123"),
|
|
84
|
-
|
|
81
|
+
Meta("viewport", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"),
|
|
85
|
-
|
|
82
|
+
Link("icon", "/assets/icon.png"),
|
|
86
|
-
|
|
83
|
+
Link("apple-touch-icon", "/assets/icon.png"),
|
|
87
|
-
|
|
84
|
+
Link("stylesheet", "/assets/styles.css"),
|
|
88
|
-
|
|
85
|
+
Script(Src("/assets/alpine.js"), Defer()),
|
|
89
|
-
|
|
86
|
+
Meta("title", "title"),
|
|
90
|
-
|
|
87
|
+
),
|
|
91
|
-
|
|
88
|
+
Body(
|
|
92
|
-
|
|
89
|
+
H1(Text("Hello this is a h1")),
|
|
93
|
-
|
|
90
|
+
H2(Text("Hello this is a h2")),
|
|
94
|
-
|
|
91
|
+
H3(XData("{ message: 'I ❤️ Alpine' }"), XText("message"), Text("")),
|
|
95
|
-
|
|
92
|
+
Counter(ctx, 4),
|
|
96
|
-
|
|
93
|
+
),
|
|
97
|
-
|
|
94
|
+
)
|
|
98
|
-
|
|
95
|
+
p.WriteHtml(b)
|
|
99
|
-
|
|
96
|
+
c := cupaloy.New(cupaloy.SnapshotFileExtension(".html"))
|
|
100
|
-
|
|
97
|
+
c.SnapshotT(t, b.String())
|
|
98
|
+
})
|
|
99
|
+
})
|
|
101
100
|
}
|