~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.
06e8bcb7
—
Peter John 3 years ago
fix example
- _example/components/Checkbox.go +1 -1
- _example/components/todo.go +7 -7
- _example/containers/Error.go +2 -2
- _example/containers/TodoCount.go +3 -3
- _example/containers/TodoList.go +7 -3
- _example/routes/404/get.go +1 -1
- _example/routes/about/get.go +1 -1
- _example/routes/get.go +32 -19
- _example/routes/post.go +12 -20
- http.go +3 -1
_example/components/Checkbox.go
CHANGED
|
@@ -6,7 +6,7 @@ import (
|
|
|
6
6
|
|
|
7
7
|
// var CheckboxStyles = M{}
|
|
8
8
|
|
|
9
|
-
func Checkbox(c *Context, value bool) *
|
|
9
|
+
func Checkbox(c *Context, value bool) []*Tag {
|
|
10
10
|
return c.Render(`
|
|
11
11
|
<input class="checkbox" type="checkbox" checked="{value}" />
|
|
12
12
|
`)
|
_example/components/todo.go
CHANGED
|
@@ -11,32 +11,32 @@ var TodoStyles = M{
|
|
|
11
11
|
"button-1": "ml-4 text-gray-400",
|
|
12
12
|
"label": "flex-1 min-w-0 flex items-center break-all ml-2 p-2 text-gray-800",
|
|
13
13
|
"striked": "text-gray-500 line-through",
|
|
14
|
-
"button-2": "mr-4 text-red-700 opacity-0
|
|
14
|
+
"button-2": "mr-4 text-red-700 opacity-0 hover:opacity-100",
|
|
15
15
|
"unchecked": "text-gray-200",
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
func Todo(c *Context, todo *todos.Todo) *
|
|
18
|
+
func Todo(c *Context, todo *todos.Todo) []*Tag {
|
|
19
19
|
checked := "/icons/unchecked.svg?fill=gray-400"
|
|
20
20
|
if todo.Completed {
|
|
21
21
|
checked = "/icons/checked.svg?fill=green-500"
|
|
22
22
|
}
|
|
23
23
|
c.Set("checked", checked)
|
|
24
24
|
return c.Render(`
|
|
25
|
-
<div id="todo-{todo.ID}" class="
|
|
25
|
+
<div id="todo-{todo.ID}" class="Todo">
|
|
26
26
|
<div class="row">
|
|
27
27
|
<form hx-post="/" hx-target="#todo-{todo.ID}" hx-swap="outerHTML">
|
|
28
28
|
<input type="hidden" name="intent" value="complete" />
|
|
29
|
-
<input type="hidden" name="id" value=
|
|
29
|
+
<input type="hidden" name="id" value={todo.ID} />
|
|
30
30
|
<button class="button-1">
|
|
31
|
-
<img src=
|
|
31
|
+
<img src={checked} width="24" height="24" />
|
|
32
32
|
</button>
|
|
33
33
|
</form>
|
|
34
|
-
<label class=
|
|
34
|
+
<label class={ "label": true, "striked": todo.Completed }>
|
|
35
35
|
{todo.Text}
|
|
36
36
|
</label>
|
|
37
37
|
<form hx-post="/" hx-target="#todo-{todo.ID}" hx-swap="delete">
|
|
38
38
|
<input type="hidden" name="intent" value="delete" />
|
|
39
|
-
<input type="hidden" name="id" value=
|
|
39
|
+
<input type="hidden" name="id" value={todo.ID} />
|
|
40
40
|
<button class="button-2">
|
|
41
41
|
<img src="/icons/close.svg?fill=red-500" width="24" height="24" />
|
|
42
42
|
</button>
|
_example/containers/Error.go
CHANGED
|
@@ -4,10 +4,10 @@ import (
|
|
|
4
4
|
. "github.com/pyros2097/gromer/gsx"
|
|
5
5
|
)
|
|
6
6
|
|
|
7
|
-
func Error(c *Context, err error) *
|
|
7
|
+
func Error(c *Context, err error) []*Tag {
|
|
8
8
|
c.Set("err", err.Error())
|
|
9
9
|
return c.Render(`
|
|
10
|
-
<span class="
|
|
10
|
+
<span class="Error">
|
|
11
11
|
<strong>Failed to load: {err}</strong>
|
|
12
12
|
</span>
|
|
13
13
|
`)
|
_example/containers/TodoCount.go
CHANGED
|
@@ -5,7 +5,7 @@ import (
|
|
|
5
5
|
. "github.com/pyros2097/gromer/gsx"
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
func TodoCount(c *Context, filter string) *
|
|
8
|
+
func TodoCount(c *Context, filter string) []*Tag {
|
|
9
9
|
todos, err := todos.GetAllTodo(c, todos.GetAllTodoParams{
|
|
10
10
|
Filter: filter,
|
|
11
11
|
Limit: 1000,
|
|
@@ -21,8 +21,8 @@ func TodoCount(c *Context, filter string) *Node {
|
|
|
21
21
|
}
|
|
22
22
|
c.Set("count", count)
|
|
23
23
|
return c.Render(`
|
|
24
|
-
<span id="todo-count" class="
|
|
24
|
+
<span id="todo-count" class="TodoCount" hx-swap-oob="true">
|
|
25
|
-
<strong>{count}</strong> item left
|
|
25
|
+
<strong>{count}</strong>" item left"
|
|
26
26
|
</span>
|
|
27
27
|
`)
|
|
28
28
|
}
|
_example/containers/TodoList.go
CHANGED
|
@@ -10,7 +10,7 @@ var TodoListStyles = M{
|
|
|
10
10
|
"container": "list-none",
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
func TodoList(c *Context, page int, filter string) *
|
|
13
|
+
func TodoList(c *Context, page int, filter string) []*Tag {
|
|
14
14
|
index := Default(page, 1)
|
|
15
15
|
todos, err := todos.GetAllTodo(c, todos.GetAllTodoParams{
|
|
16
16
|
Filter: filter,
|
|
@@ -21,8 +21,12 @@ func TodoList(c *Context, page int, filter string) *Node {
|
|
|
21
21
|
}
|
|
22
22
|
c.Set("todos", todos)
|
|
23
23
|
return c.Render(`
|
|
24
|
-
<ul id="todo-list" class="
|
|
24
|
+
<ul id="todo-list" class="TodoList">
|
|
25
|
+
for i, v := range todos {
|
|
26
|
+
return (
|
|
25
|
-
|
|
27
|
+
<Todo todo={v} />
|
|
28
|
+
)
|
|
29
|
+
}
|
|
26
30
|
</ul>
|
|
27
31
|
`)
|
|
28
32
|
}
|
_example/routes/404/get.go
CHANGED
|
@@ -11,7 +11,7 @@ var (
|
|
|
11
11
|
Styles = M{}
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
func GET(c *Context) (*
|
|
14
|
+
func GET(c *Context) ([]*Tag, int, error) {
|
|
15
15
|
return c.Render(`
|
|
16
16
|
<main class="box center">
|
|
17
17
|
<h1>Page Not Found</h1>
|
_example/routes/about/get.go
CHANGED
|
@@ -12,7 +12,7 @@ var (
|
|
|
12
12
|
Styles = M{}
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
func GET(c *Context) (*
|
|
15
|
+
func GET(c *Context) ([]*Tag, int, error) {
|
|
16
16
|
return c.Render(`
|
|
17
17
|
<div class="flex flex-col justify-center items-center">
|
|
18
18
|
A new link is here
|
_example/routes/get.go
CHANGED
|
@@ -48,12 +48,25 @@ type GetParams struct {
|
|
|
48
48
|
Filter string `json:"filter"`
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
func getActive(v bool) string {
|
|
52
|
+
if v {
|
|
53
|
+
return "active"
|
|
54
|
+
}
|
|
55
|
+
return ""
|
|
56
|
+
}
|
|
57
|
+
|
|
51
|
-
func GET(c *Context, params GetParams) (*
|
|
58
|
+
func GET(c *Context, params GetParams) ([]*Tag, int, error) {
|
|
59
|
+
allClass := getActive(params.Filter == "all")
|
|
60
|
+
activeClass := getActive(params.Filter == "active")
|
|
61
|
+
completedClass := getActive(params.Filter == "completed")
|
|
62
|
+
c.Set("allClass", allClass)
|
|
63
|
+
c.Set("activeClass", activeClass)
|
|
64
|
+
c.Set("completedClass", completedClass)
|
|
52
65
|
return c.Render(`
|
|
53
|
-
<div class="bg">
|
|
66
|
+
<div id="bg" class="bg">
|
|
54
67
|
<div class="container">
|
|
55
68
|
<header>
|
|
56
|
-
<h1 class="title">todos</h1>
|
|
69
|
+
<h1 class="title">"todos"</h1>
|
|
57
70
|
</header>
|
|
58
71
|
<main class="main">
|
|
59
72
|
<div class="input-box">
|
|
@@ -65,45 +78,45 @@ func GET(c *Context, params GetParams) (*Node, int, error) {
|
|
|
65
78
|
</form>
|
|
66
79
|
<form class="input-form" hx-post="/" hx-target="#todo-list" hx-swap="afterbegin" _="on htmx:afterOnLoad set #text.value to ''">
|
|
67
80
|
<input type="hidden" name="intent" value="create" />
|
|
68
|
-
<input id="text" name="text" class="input" placeholder="What needs to be done?" autocomplete="off">
|
|
81
|
+
<input id="text" name="text" class="input" placeholder="What needs to be done?" autocomplete="off" />
|
|
69
82
|
</form>
|
|
70
83
|
</div>
|
|
71
|
-
<TodoList id="todo-list" page=
|
|
84
|
+
<TodoList id="todo-list" page={params.Page} filter={params.Filter} />
|
|
72
85
|
<div class="bottom">
|
|
73
86
|
<div class="section-1">
|
|
74
|
-
<TodoCount filter=
|
|
87
|
+
<TodoCount filter={params.Filter} />
|
|
75
88
|
</div>
|
|
76
|
-
<ul class="section-2">
|
|
89
|
+
<ul class="section-2" hx-boost="true">
|
|
77
90
|
<li>
|
|
78
|
-
<a href="?filter=all" class="link
|
|
91
|
+
<a href="?filter=all" class="link {allClass}">"All"</a>
|
|
79
92
|
</li>
|
|
80
93
|
<li>
|
|
81
|
-
<a href="?filter=active" class="link">Active</a>
|
|
94
|
+
<a href="?filter=active" class="link {activeClass}">"Active"</a>
|
|
82
95
|
</li>
|
|
83
96
|
<li>
|
|
84
|
-
<a href="?filter=completed" class="link">Completed</a>
|
|
97
|
+
<a href="?filter=completed" class="link {completedClass}">"Completed"</a>
|
|
85
98
|
</li>
|
|
86
99
|
</ul>
|
|
87
100
|
<div class="section-3">
|
|
88
101
|
<form hx-target="#todo-list" hx-post="/">
|
|
89
102
|
<input type="hidden" name="intent" value="clear_completed" />
|
|
90
|
-
<button type="submit" class="bottom-clear">Clear completed</button>
|
|
103
|
+
<button type="submit" class="bottom-clear">"Clear completed"</button>
|
|
91
104
|
</form>
|
|
92
105
|
</div>
|
|
93
106
|
</div>
|
|
94
107
|
</main>
|
|
95
108
|
<footer class="footer">
|
|
96
|
-
<span class="subtitle">Written by
|
|
109
|
+
<span class="subtitle">"Written by "
|
|
97
|
-
<a class="link" href="https://github.com/pyrossh/">pyrossh</a>
|
|
110
|
+
<a class="link" href="https://github.com/pyrossh/">"pyrossh"</a>
|
|
98
111
|
</span>
|
|
99
|
-
<span class="subtitle">using
|
|
112
|
+
<span class="subtitle">"using "
|
|
100
|
-
<a class="link" href="https://github.com/pyrossh/gromer">Gromer</a>
|
|
113
|
+
<a class="link" href="https://github.com/pyrossh/gromer">"Gromer"</a>
|
|
101
114
|
</span>
|
|
102
|
-
<span class="subtitle">thanks to
|
|
115
|
+
<span class="subtitle">"thanks to"
|
|
103
|
-
<a class="link" href="https://github.com/wishawa/">Wisha Wa</a>
|
|
116
|
+
<a class="link" href="https://github.com/wishawa/">"Wisha Wa"</a>
|
|
104
117
|
</span>
|
|
105
|
-
<span class="subtitle">according to the spec
|
|
118
|
+
<span class="subtitle">"according to the spec "
|
|
106
|
-
<a class="link" href="https://todomvc.com/">TodoMVC</a>
|
|
119
|
+
<a class="link" href="https://todomvc.com/">"TodoMVC"</a>
|
|
107
120
|
</span>
|
|
108
121
|
</footer>
|
|
109
122
|
</div>
|
_example/routes/post.go
CHANGED
|
@@ -14,7 +14,7 @@ type PostParams struct {
|
|
|
14
14
|
Text string `json:"text"`
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
func POST(c *Context, params PostParams) (*
|
|
17
|
+
func POST(c *Context, params PostParams) ([]*Tag, int, error) {
|
|
18
18
|
if params.Intent == "select_all" {
|
|
19
19
|
allTodos, err := todos.GetAllTodo(c, todos.GetAllTodoParams{
|
|
20
20
|
Filter: "all",
|
|
@@ -33,13 +33,11 @@ func POST(c *Context, params PostParams) (*Node, int, error) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
return c.Render(`
|
|
36
|
-
<div>
|
|
37
|
-
<TodoList id="todo-list" filter="all" page="1"></TodoList>
|
|
38
|
-
|
|
36
|
+
<TodoCount filter="all" page="1" />
|
|
39
|
-
|
|
37
|
+
<button id="check-all" class="button" hx-swap-oob="true">
|
|
40
|
-
|
|
38
|
+
<img src="/icons/check-all.svg?fill=green-500" />
|
|
41
|
-
|
|
39
|
+
</button>
|
|
42
|
-
</
|
|
40
|
+
<TodoList id="todo-list" filter="all" page="1" />
|
|
43
41
|
`), 200, nil
|
|
44
42
|
} else if params.Intent == "clear_completed" {
|
|
45
43
|
allTodos, err := todos.GetAllTodo(c, todos.GetAllTodoParams{
|
|
@@ -58,10 +56,8 @@ func POST(c *Context, params PostParams) (*Node, int, error) {
|
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
return c.Render(`
|
|
61
|
-
<
|
|
59
|
+
<TodoCount filter="all" page="1" />
|
|
62
|
-
|
|
60
|
+
<TodoList id="todo-list" filter="all" page="1" />
|
|
63
|
-
<TodoCount filter="all" page="1"></TodoCount>
|
|
64
|
-
</div>
|
|
65
61
|
`), 200, nil
|
|
66
62
|
} else if params.Intent == "create" {
|
|
67
63
|
todo, err := todos.CreateTodo(c, params.Text)
|
|
@@ -70,10 +66,8 @@ func POST(c *Context, params PostParams) (*Node, int, error) {
|
|
|
70
66
|
}
|
|
71
67
|
c.Set("todo", todo)
|
|
72
68
|
return c.Render(`
|
|
73
|
-
<div>
|
|
74
|
-
<Todo></Todo>
|
|
75
|
-
|
|
69
|
+
<TodoCount filter="all" page="1" />
|
|
76
|
-
</
|
|
70
|
+
<Todo />
|
|
77
71
|
`), 200, nil
|
|
78
72
|
} else if params.Intent == "delete" {
|
|
79
73
|
_, err := todos.DeleteTodo(c, params.ID)
|
|
@@ -95,10 +89,8 @@ func POST(c *Context, params PostParams) (*Node, int, error) {
|
|
|
95
89
|
}
|
|
96
90
|
c.Set("todo", todo)
|
|
97
91
|
return c.Render(`
|
|
98
|
-
<div>
|
|
99
|
-
<Todo></Todo>
|
|
100
|
-
|
|
92
|
+
<TodoCount filter="all" page="1" />
|
|
101
|
-
</
|
|
93
|
+
<Todo />
|
|
102
94
|
`), 200, nil
|
|
103
95
|
}
|
|
104
96
|
return nil, 404, fmt.Errorf("Intent not specified: %s", params.Intent)
|
http.go
CHANGED
|
@@ -196,7 +196,9 @@ func PerformRequest(route string, h interface{}, c *gsx.Context, w http.Response
|
|
|
196
196
|
w.Header().Set("Content-Type", "text/html")
|
|
197
197
|
// This has to be at end always
|
|
198
198
|
w.WriteHeader(responseStatus)
|
|
199
|
+
if responseStatus != 204 {
|
|
199
|
-
|
|
200
|
+
gsx.Write(c, w, response.([]*gsx.Tag))
|
|
201
|
+
}
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
func LogMiddleware(next http.Handler) http.Handler {
|