diff --git a/.vscode/examples.code-snippets b/.vscode/examples.code-snippets index e9e6f88..18c80d5 100644 --- a/.vscode/examples.code-snippets +++ b/.vscode/examples.code-snippets @@ -89,9 +89,9 @@ "", "import (", " \"embed\"", - " \"time\"", "", - " \"github.com/lithammer/dedent\"", + "g \"github.com/maragudk/gomponents\"", + ". \"github.com/maragudk/gomponents/html\"", " g \"github.com/maragudk/gomponents\"", " \"github.com/will-wow/typed-htmx-go/examples/web/$2/shared\"", " \"github.com/will-wow/typed-htmx-go/examples/web/exprint\"", @@ -102,7 +102,7 @@ "", "var hx = htmx.NewGomponents()", "", - "//go:embed ${TM_DIRECTORY/.*\\/(.*)$/$1/}.gom.go", + "//go:embed $TM_FILENAME", "var fs embed.FS", "var ex = exprint.New(fs, \"//\", \"\")", "", @@ -110,13 +110,14 @@ " return layout.Wrapper(", " \"$1\",", " H1(g.Text(\"$1\")),", + " Class(\"$3\"),", " P(", " g.Text(\"Desc\"),", " ),", " Pre(", " Code(", " Class(\"language-go\"),", - " g.Text(ex.PrintOrErr(\"$TM_FILENAME_BASE.gom.go\", \"demo\")),", + " g.Text(ex.PrintOrErr(\"$TM_FILENAME_BASE\", \"demo\")),", " ),", " ),", " H2(g.Text(\"Demo\")),", diff --git a/examples/cmd/server/main.go b/examples/cmd/server/main.go index 84a21c4..0b376e4 100644 --- a/examples/cmd/server/main.go +++ b/examples/cmd/server/main.go @@ -16,7 +16,7 @@ func main() { server := &http.Server{ Addr: "localhost:8080", Handler: handler, - ReadTimeout: time.Second * 30, + ReadTimeout: time.Second * 10, WriteTimeout: time.Second * 10, } diff --git a/examples/vercel.json b/examples/vercel.json index 6bba80b..dc23698 100644 --- a/examples/vercel.json +++ b/examples/vercel.json @@ -2,7 +2,7 @@ "routes": [{ "src": "/(.*)", "dest": "/api" }], "functions": { "api/index.go": { - "maxDuration": 30 + "maxDuration": 10 } } } diff --git a/examples/web/examples/exgom/examples.gom.go b/examples/web/examples/exgom/examples.gom.go index 2e4ab3b..c428709 100644 --- a/examples/web/examples/exgom/examples.gom.go +++ b/examples/web/examples/exgom/examples.gom.go @@ -1,9 +1,12 @@ package exgom import ( + "fmt" + g "github.com/maragudk/gomponents" . "github.com/maragudk/gomponents/html" + "github.com/will-wow/typed-htmx-go/examples/web/examples/registry" "github.com/will-wow/typed-htmx-go/examples/web/layout/gom/layout" ) @@ -29,31 +32,13 @@ func Page() g.Node { ), ), TBody( - exampleRow( - "/examples/gomponents/click-to-edit", - "Click To Edit", - "Demonstrates inline editing of a data object", - ), - exampleRow( - "/examples/gomponents/bulk-update", - "Bulk Update", - "Demonstrates bulk updating of multiple rows of data", - ), - exampleRow( - "/examples/gomponents/active-search/", - "Active Search", - "Demonstrates the active search box pattern", - ), - exampleRow( - "/examples/gomponents/progress-bar/", - "Progress Bar", - "Demonstrates a job-runner like progress bar", - ), - exampleRow( - "/examples/gomponents/class-tools/", - "Class Tools", - "Demo of class-tools options", - ), + g.Group(g.Map(registry.Examples, func(ex registry.Example) g.Node { + return exampleRow( + fmt.Sprintf("/examples/gomponents/%s/", ex.Slug), + ex.Title, + ex.Desc, + ) + })), ), ), ) diff --git a/examples/web/examples/extempl/examples.templ b/examples/web/examples/extempl/examples.templ index 77eb722..f92d862 100644 --- a/examples/web/examples/extempl/examples.templ +++ b/examples/web/examples/extempl/examples.templ @@ -1,7 +1,9 @@ package extempl import ( + "fmt" "github.com/will-wow/typed-htmx-go/examples/web/layout/templ/layout" + "github.com/will-wow/typed-htmx-go/examples/web/examples/registry" ) templ Page() { @@ -32,31 +34,13 @@ templ Page() {
- @exampleRow( - "/examples/templ/click-to-edit/", - "Click To Edit", - "Demonstrates inline editing of a data object", - ) - @exampleRow( - "/examples/templ/bulk-update/", - "Bulk Update", - "Demonstrates bulk updating of multiple rows of data", - ) - @exampleRow( - "/examples/templ/active-search/", - "Active Search", - "Demonstrates the active search box pattern", - ) - @exampleRow( - "/examples/templ/progress-bar/", - "Progress Bar", - "Demonstrates a job-runner like progress bar", - ) - @exampleRow( - "/examples/templ/class-tools/", - "Class Tools", - "Demo of class-tools options", - ) + for _, ex := range registry.Examples { + @exampleRow( + fmt.Sprintf("/examples/templ/%s/", ex.Slug), + ex.Title, + ex.Desc, + ) + } } diff --git a/examples/web/examples/extempl/examples_templ.go b/examples/web/examples/extempl/examples_templ.go index cfa2cd8..515ee2e 100644 --- a/examples/web/examples/extempl/examples_templ.go +++ b/examples/web/examples/extempl/examples_templ.go @@ -8,10 +8,12 @@ package extempl import ( "bytes" "context" + "fmt" "io" "github.com/a-h/templ" + "github.com/will-wow/typed-htmx-go/examples/web/examples/registry" "github.com/will-wow/typed-htmx-go/examples/web/layout/templ/layout" ) @@ -38,45 +40,15 @@ func Page() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = exampleRow( - "/examples/templ/click-to-edit/", - "Click To Edit", - "Demonstrates inline editing of a data object", - ).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = exampleRow( - "/examples/templ/bulk-update/", - "Bulk Update", - "Demonstrates bulk updating of multiple rows of data", - ).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = exampleRow( - "/examples/templ/active-search/", - "Active Search", - "Demonstrates the active search box pattern", - ).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = exampleRow( - "/examples/templ/progress-bar/", - "Progress Bar", - "Demonstrates a job-runner like progress bar", - ).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = exampleRow( - "/examples/templ/class-tools/", - "Class Tools", - "Demo of class-tools options", - ).Render(ctx, templ_7745c5c3_Buffer) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err + for _, ex := range registry.Examples { + templ_7745c5c3_Err = exampleRow( + fmt.Sprintf("/examples/templ/%s/", ex.Slug), + ex.Title, + ex.Desc, + ).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { @@ -127,7 +99,7 @@ func exampleRow(link, name, description string) templ.Component { var templ_7745c5c3_Var5 string templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/examples/extempl/examples.templ`, Line: 68, Col: 41} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/examples/extempl/examples.templ`, Line: 52, Col: 41} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { @@ -140,7 +112,7 @@ func exampleRow(link, name, description string) templ.Component { var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(description) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/examples/extempl/examples.templ`, Line: 71, Col: 16} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/examples/extempl/examples.templ`, Line: 55, Col: 16} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { diff --git a/examples/web/examples/registry/registry.go b/examples/web/examples/registry/registry.go new file mode 100644 index 0000000..55aea5e --- /dev/null +++ b/examples/web/examples/registry/registry.go @@ -0,0 +1,58 @@ +package registry + +import ( + "net/http" + + "github.com/will-wow/typed-htmx-go/examples/web/activesearch" + "github.com/will-wow/typed-htmx-go/examples/web/bulkupdate" + "github.com/will-wow/typed-htmx-go/examples/web/classtools_ex" + "github.com/will-wow/typed-htmx-go/examples/web/clicktoedit" + "github.com/will-wow/typed-htmx-go/examples/web/progressbar" + "github.com/will-wow/typed-htmx-go/examples/web/sse_ex" +) + +type Example struct { + Title string + Desc string + Slug string + Handler func(bool) http.Handler +} + +var Examples = []Example{ + { + Title: "Click to Edit", + Desc: "Demonstrates inline editing of a data object", + Slug: "click-to-edit", + Handler: clicktoedit.NewHandler, + }, + { + Title: "Bulk Update", + Desc: "Demonstrates bulk updating of multiple rows of data", + Slug: "bulk-update", + Handler: bulkupdate.NewHandler, + }, + { + Title: "Active Search", + Desc: "Demonstrates the active search box pattern", + Slug: "active-search", + Handler: activesearch.NewHandler, + }, + { + Title: "Progress Bar", + Desc: "Demonstrates a job-runner like progress bar", + Slug: "progress-bar", + Handler: progressbar.NewHandler, + }, + { + Title: "Class Tools", + Desc: "Demo of class-tools options", + Slug: "class-tools", + Handler: classtools_ex.NewHandler, + }, + { + Title: "Server-Sent Events", + Desc: "Streaming responses with the SSE HTMX Extension", + Slug: "sse", + Handler: sse_ex.NewHandler, + }, +} diff --git a/examples/web/layout/gom/layout/layout.gom.go b/examples/web/layout/gom/layout/layout.gom.go index 6a4921e..9e0c423 100644 --- a/examples/web/layout/gom/layout/layout.gom.go +++ b/examples/web/layout/gom/layout/layout.gom.go @@ -33,8 +33,9 @@ func Wrapper(title string, children ...g.Node) g.Node { Meta(Name("htmx-config"), hx.Config( hxconfig.New().IncludeIndicatorStyles(false), )), - Script(Src("https://unpkg.com/htmx.org@1.9.10")), - Script(Src("https://unpkg.com/htmx.org@1.9.10/dist/ext/class-tools.js")), + Script(Src("https://unpkg.com/htmx.org@1.9.12")), + Script(Src("https://unpkg.com/htmx.org@1.9.12/dist/ext/class-tools.js")), + Script(Src("https://unpkg.com/htmx.org@1.9.12/dist/ext/sse.js")), Link(Rel("stylesheet"), Href("https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css")), Link(Rel("stylesheet"), Href("https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/default.min.css")), Link(Rel("stylesheet"), Href("/static/main.css")), diff --git a/examples/web/layout/templ/layout/layout.templ b/examples/web/layout/templ/layout/layout.templ index badfa93..817a32a 100644 --- a/examples/web/layout/templ/layout/layout.templ +++ b/examples/web/layout/templ/layout/layout.templ @@ -30,8 +30,8 @@ templ Wrapper(title string, className ...string) { hxconfig.New().Timeout(time.Second), )... } /> - - + + hx.Swap(swap.OuterHTML)")), + g.Text("."), + ), + H2(g.Text("Demo")), + Trigger(), + ) +} + +func Trigger() g.Node { + //ex:start:trigger + return Button( + hx.Get("/examples/gomponents/sse/countdown/"), + hx.Target(htmx.TargetThis), + hx.Swap(swap.OuterHTML), + g.Text("Start Countdown"), + ) + //ex:end:trigger +} + +func Countdown() g.Node { + //ex:start:countdown + return Div( + hx.Ext(sse.Extension), + sse.Connect(hx, "/examples/gomponents/sse/countdown/feed/"), + sse.Swap(hx, shared.ResetEvent), + hx.Swap(swap.OuterHTML), + Div( + sse.Swap(hx, shared.CountdownEvent), + hx.Swap(swap.InnerHTML), + ), + ) + //ex:end:countdown +} + +//ex:start:message +func Message(msg string) g.Node { + return P(g.Text(msg)) +} + +func Blastoff() g.Node { + return P(g.Text("Blastoff!")) +} + +//ex:end:message diff --git a/examples/web/sse_ex/extempl/sse.templ b/examples/web/sse_ex/extempl/sse.templ index 1be7509..dd4d2a5 100644 --- a/examples/web/sse_ex/extempl/sse.templ +++ b/examples/web/sse_ex/extempl/sse.templ @@ -8,8 +8,7 @@ import ( "github.com/will-wow/typed-htmx-go/htmx/ext/sse" "github.com/will-wow/typed-htmx-go/htmx" "github.com/will-wow/typed-htmx-go/htmx/swap" - "github.com/will-wow/typed-htmx-go/examples/web/sse_ex/chatroom" - "github.com/will-wow/typed-htmx-go/htmx/on" + "github.com/will-wow/typed-htmx-go/examples/web/sse_ex/shared" ) var hx = htmx.NewTempl() @@ -19,29 +18,29 @@ var fs embed.FS var ex = exprint.New(fs, "//", "") templ Page() { - @layout.Wrapper("Server-Side Events") { -- A demo of Server-Side Events using htmx and typed-htmx-go. + A demo countdown using Server-Sent Events, with htmx and typed-htmx-go.
- When you click the button below, that fetches a new element that uses the sse extension to start a live feed. + When you click the button below, that fetches a new element that uses the sse extension to start stream a countdown.
- { ex.PrintOrErr("sse.templ", "entry") }
+ { ex.PrintOrErr("sse.templ", "trigger") }
- The new elements uses the sse.Connect attribute to connect to a server-side event stream, and allows you to POST messages to the stream. + The new elements uses the sse.Connect attribute to connect to a server-side event streaming countdown.
- { ex.PrintOrErr("sse.templ", "chatroom") }
+ { ex.PrintOrErr("sse.templ", "countdown") }
- When you or another user posts a message, it will be sent to the server and broadcast to all connected clients as a simple div. + Each second, the server sends a countdown message that updates the innerHTML of the div.
@@ -49,60 +48,44 @@ templ Page() {
- After 25 seconds, the server will send the EndEvent, that closes removes the sse connection by replacing the sse.Connect element with the initial button using hx.Swap(swap.OuterHTML)
.
+ After the countdown is complete, the server will send a ResetEvent, that closes removes the sse connection by replacing the sse.Connect element with the initial button using hx.Swap(swap.OuterHTML)
.
{ msg }
} //ex:end:message diff --git a/examples/web/sse_ex/extempl/sse_templ.go b/examples/web/sse_ex/extempl/sse_templ.go index 8c5a325..246abc5 100644 --- a/examples/web/sse_ex/extempl/sse_templ.go +++ b/examples/web/sse_ex/extempl/sse_templ.go @@ -14,12 +14,11 @@ import ( "github.com/a-h/templ" "github.com/will-wow/typed-htmx-go/htmx" "github.com/will-wow/typed-htmx-go/htmx/ext/sse" - "github.com/will-wow/typed-htmx-go/htmx/on" "github.com/will-wow/typed-htmx-go/htmx/swap" "github.com/will-wow/typed-htmx-go/examples/web/exprint" "github.com/will-wow/typed-htmx-go/examples/web/layout/templ/layout" - "github.com/will-wow/typed-htmx-go/examples/web/sse_ex/chatroom" + "github.com/will-wow/typed-htmx-go/examples/web/sse_ex/shared" ) var hx = htmx.NewTempl() @@ -47,50 +46,50 @@ func Page() templ.Component { templ_7745c5c3_Buffer = templ.GetBuffer() defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("A demo of Server-Side Events using htmx and typed-htmx-go.
When you click the button below, that fetches a new element that uses the sse extension to start a live feed.
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Server-Sent Events
A demo countdown using Server-Sent Events, with htmx and typed-htmx-go.
When you click the button below, that fetches a new element that uses the sse extension to start stream a countdown.
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
- templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(ex.PrintOrErr("sse.templ", "entry"))
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(ex.PrintOrErr("sse.templ", "trigger"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 32, Col: 41}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 31, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
The new elements uses the sse.Connect attribute to connect to a server-side event stream, and allows you to POST messages to the stream.
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
The new elements uses the sse.Connect attribute to connect to a server-side event streaming countdown.
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
- templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(ex.PrintOrErr("sse.templ", "chatroom"))
+ templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(ex.PrintOrErr("sse.templ", "countdown"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 40, Col: 44}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 39, Col: 45}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
When you or another user posts a message, it will be sent to the server and broadcast to all connected clients as a simple div.
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Each second, the server sends a countdown message that updates the innerHTML of the div.
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(ex.PrintOrErr("sse.templ", "message"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 48, Col: 43}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `web/sse_ex/extempl/sse.templ`, Line: 47, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
After 25 seconds, the server will send the EndEvent, that closes removes the sse connection by replacing the sse.Connect element with the initial button using hx.Swap(swap.OuterHTML)
.
Demo
")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
After the countdown is complete, the server will send a ResetEvent, that closes removes the sse connection by replacing the sse.Connect element with the initial button using hx.Swap(swap.OuterHTML)
.