~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.


06e8bcb7 Peter John

3 years ago
fix example
_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) *Node {
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 group-hover-opacity-100",
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) *Node {
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="todo">
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="{todo.ID}" />
29
+ <input type="hidden" name="id" value={todo.ID} />
30
30
  <button class="button-1">
31
- <img src="{checked}" width="24" height="24" />
31
+ <img src={checked} width="24" height="24" />
32
32
  </button>
33
33
  </form>
34
- <label class="{ label: true, striked: todo.Completed }">
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="{todo.ID}" />
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) *Node {
7
+ func Error(c *Context, err error) []*Tag {
8
8
  c.Set("err", err.Error())
9
9
  return c.Render(`
10
- <span class="error">
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) *Node {
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="todo-count" hx-swap-oob="true">
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) *Node {
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="todolist" x-for="todo in todos">
24
+ <ul id="todo-list" class="TodoList">
25
+ for i, v := range todos {
26
+ return (
25
- <Todo />
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) (*Node, int, error) {
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) (*Node, int, error) {
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) (*Node, int, error) {
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="{params.Page}" filter="{params.Filter}"></TodoList>
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="{params.Filter}"></TodoCount>
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 active">All</a>
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) (*Node, int, error) {
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
- <TodoCount filter="all" page="1"></TodoCount>
36
+ <TodoCount filter="all" page="1" />
39
- <button id="check-all" class="button" hx-swap-oob="true">
37
+ <button id="check-all" class="button" hx-swap-oob="true">
40
- <img src="/icons/check-all.svg?fill=green-500" />
38
+ <img src="/icons/check-all.svg?fill=green-500" />
41
- </button>
39
+ </button>
42
- </div>
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
- <div>
59
+ <TodoCount filter="all" page="1" />
62
- <TodoList id="todo-list" filter="all" page="1"></TodoList>
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
- <TodoCount filter="all" page="1"></TodoCount>
69
+ <TodoCount filter="all" page="1" />
76
- </div>
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
- <TodoCount filter="all" page="1"></TodoCount>
92
+ <TodoCount filter="all" page="1" />
101
- </div>
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
- gsx.Write(c, w, response.([]*gsx.Tag))
200
+ gsx.Write(c, w, response.([]*gsx.Tag))
201
+ }
200
202
  }
201
203
 
202
204
  func LogMiddleware(next http.Handler) http.Handler {