Skip to content

Commit

Permalink
Merge pull request #47 from chasefleming/chasefleming/todo-example
Browse files Browse the repository at this point in the history
Add Todo List Example
  • Loading branch information
chasefleming authored Oct 31, 2023
2 parents aada551 + d7696ac commit 717ccf0
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
21 changes: 21 additions & 0 deletions examples/htmx-fiber-todo/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module htmx-fiber-todo

go 1.21.1

require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/chasefleming/elem-go v0.4.0 // indirect
github.com/gofiber/fiber/v2 v2.50.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.50.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.13.0 // indirect
)

replace github.com/chasefleming/elem-go => ../../../elem-go
29 changes: 29 additions & 0 deletions examples/htmx-fiber-todo/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/chasefleming/elem-go v0.4.0 h1:SjfVfITUpayyQR/q0e15vD/rik3oQ+lQU8YXixn/M24=
github.com/chasefleming/elem-go v0.4.0/go.mod h1:hz73qILBIKnTgOujnSMtEj20/epI+f6vg71RUilJAA4=
github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
175 changes: 175 additions & 0 deletions examples/htmx-fiber-todo/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package main

import (
"github.com/chasefleming/elem-go/htmx"
"strconv"

"github.com/chasefleming/elem-go"
"github.com/chasefleming/elem-go/attrs"
"github.com/chasefleming/elem-go/styles"
"github.com/gofiber/fiber/v2"
)

// Todo model
type Todo struct {
ID int
Title string
Done bool
}

// Global todos slice (for simplicity)
var todos = []Todo{
{ID: 1, Title: "First task", Done: false},
{ID: 2, Title: "Second task", Done: true},
}

func main() {
app := fiber.New()

// Routes
app.Get("/", renderTodosRoute)
app.Post("/toggle/:id", toggleTodoRoute)
app.Post("/add", addTodoRoute)

app.Listen(":3000")
}

func renderTodosRoute(c *fiber.Ctx) error {
c.Type("html")
return c.SendString(renderTodos(todos))
}

func toggleTodoRoute(c *fiber.Ctx) error {
id, _ := strconv.Atoi(c.Params("id"))
var updatedTodo Todo
for i, todo := range todos {
if todo.ID == id {
todos[i].Done = !todo.Done
updatedTodo = todos[i]
break
}
}
c.Type("html")
return c.SendString(renderSingleTodo(updatedTodo))
}

func addTodoRoute(c *fiber.Ctx) error {
newTitle := c.FormValue("newTodo")
if newTitle != "" {
todos = append(todos, Todo{ID: len(todos) + 1, Title: newTitle, Done: false})
}
return c.Redirect("/")
}

func getTodoAttributes(todo Todo) (elem.Attrs, elem.Attrs) {
checkboxAttributes := elem.Attrs{
attrs.Type: "checkbox",
htmx.HXPost: "/toggle/" + strconv.Itoa(todo.ID),
htmx.HXTrigger: "change",
htmx.HXIndicator: "#saving-indicator",
}

textAttributes := elem.Attrs{}
if todo.Done {
checkboxAttributes[attrs.Checked] = "checked"
textAttributes[attrs.Style] = elem.ApplyStyle(elem.Style{
styles.TextDecoration: "line-through",
})
}

return checkboxAttributes, textAttributes
}

func renderSingleTodo(todo Todo) string {
checkboxAttributes, textAttributes := getTodoAttributes(todo)
checkbox := elem.Input(checkboxAttributes)
todoItem := elem.Li(nil, checkbox, elem.Span(textAttributes, elem.Text(todo.Title)))
return todoItem.Render()
}

func renderTodos(todos []Todo) string {
inputButtonStyle := elem.Style{
styles.Width: "100%",
styles.Padding: "10px",
styles.MarginBottom: "10px",
styles.Border: "1px solid #ccc",
styles.BorderRadius: "4px",
styles.BackgroundColor: "#f9f9f9",
}

buttonStyle := elem.Style{
styles.BackgroundColor: "#007BFF",
styles.Color: "white",
styles.BorderStyle: "none",
styles.BorderRadius: "4px",
styles.Cursor: "pointer",
styles.Width: "100%",
styles.Padding: "8px 12px",
styles.FontSize: "14px",
styles.Height: "36px",
styles.MarginRight: "10px",
}

listContainerStyle := elem.Style{
styles.ListStyleType: "none",
styles.Padding: "0",
styles.Width: "100%",
}

centerContainerStyle := elem.Style{
styles.MaxWidth: "300px",
styles.Margin: "40px auto",
styles.Padding: "20px",
styles.Border: "1px solid #ccc",
styles.BoxShadow: "0px 0px 10px rgba(0,0,0,0.1)",
styles.BackgroundColor: "#f9f9f9",
}

headContent := elem.Head(nil,
elem.Script(elem.Attrs{attrs.Src: "https://unpkg.com/htmx.org"}),
)
bodyContent := elem.Div(
elem.Attrs{attrs.Style: elem.ApplyStyle(centerContainerStyle)},
elem.H1(nil, elem.Text("Todo List")),
elem.Form(
elem.Attrs{attrs.Method: "post", attrs.Action: "/add"},
elem.Input(
elem.Attrs{
attrs.Type: "text",
attrs.Name: "newTodo",
attrs.Placeholder: "Add new task...",
attrs.Style: elem.ApplyStyle(inputButtonStyle),
},
),
elem.Button(
elem.Attrs{
attrs.Type: "submit",
attrs.Style: elem.ApplyStyle(buttonStyle),
},
elem.Text("Add"),
),
),
elem.Ul(
elem.Attrs{attrs.Style: elem.ApplyStyle(listContainerStyle)},
renderTodoItems(todos)...,
),
elem.Span(
elem.Attrs{
attrs.ID: "saving-indicator",
attrs.Style: elem.ApplyStyle(elem.Style{styles.Display: "none"}),
},
elem.Text("Saving..."),
),
)
htmlContent := elem.Html(nil, headContent, bodyContent)

return htmlContent.Render()
}

func renderTodoItems(todos []Todo) []elem.Node {
return elem.TransformEach(todos, func(todo Todo) elem.Node {
checkboxAttributes, textAttributes := getTodoAttributes(todo)
checkbox := elem.Input(checkboxAttributes)
return elem.Li(nil, checkbox, elem.Span(textAttributes, elem.Text(todo.Title)))
})
}

0 comments on commit 717ccf0

Please sign in to comment.