From 60d3a869d2df7b1362015b8e704f581f4f5de418 Mon Sep 17 00:00:00 2001 From: joerdav <joe.davidson.21111@gmail.com> Date: Tue, 18 Apr 2023 10:23:18 +0100 Subject: [PATCH] feat: bulk update and click to edit --- README.md | 13 + bulkupdate/handlers.go | 64 +++ bulkupdate/templates.templ | 91 ++++ bulkupdate/templates_templ.go | 853 +++++++++++++++++++++++++++++ clicktoedit/handlers.go | 53 ++ clicktoedit/templates.templ | 60 +++ clicktoedit/templates_templ.go | 952 +++++++++++++++++++++++++++++++++ go.mod | 10 + go.sum | 8 + home.templ | 23 + home_templ.go | 227 ++++++++ main.go | 47 ++ shared/layout.templ | 50 ++ shared/layout_templ.go | 389 ++++++++++++++ shared/shared.go | 25 + 15 files changed, 2865 insertions(+) create mode 100644 README.md create mode 100644 bulkupdate/handlers.go create mode 100644 bulkupdate/templates.templ create mode 100644 bulkupdate/templates_templ.go create mode 100644 clicktoedit/handlers.go create mode 100644 clicktoedit/templates.templ create mode 100644 clicktoedit/templates_templ.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 home.templ create mode 100644 home_templ.go create mode 100644 main.go create mode 100644 shared/layout.templ create mode 100644 shared/layout_templ.go create mode 100644 shared/shared.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..5595d60 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Go HTMX Examples + +<https://htmx.org/examples> implemented in Go and templ. + +## Tasks + +### run + +Runs the app and looks for changes. + +``` +reflex -r '.*\.(go|templ)' -R '.*_templ\.go' -s -- sh -c 'templ generate && go run .' +``` diff --git a/bulkupdate/handlers.go b/bulkupdate/handlers.go new file mode 100644 index 0000000..2f41171 --- /dev/null +++ b/bulkupdate/handlers.go @@ -0,0 +1,64 @@ +package bulkupdate + +import ( + "log" + "net/http" + "strconv" +) + +type user struct { + name, email string + active bool +} + +var inMemDB []user = []user{ + {"Joe Smith", "joe@smith.org", true}, + {"Angie MacDowell", "angie@macdowell.org", true}, + {"Fuqua Tarketon", "fuqua@tarketon.org", true}, + {"Kim Yee", "kim@yee.org", false}, +} + +func Handlers(prefix string, mux *http.ServeMux) { + mux.HandleFunc(prefix+"/", index) + mux.HandleFunc(prefix+"/activate", putActivate) + mux.HandleFunc(prefix+"/deactivate", putDeactivate) +} + +func index(w http.ResponseWriter, r *http.Request) { + // Load users + Index(inMemDB).Render(r.Context(), w) +} + +func putActivate(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + log.Println(err) + w.WriteHeader(500) + return + } + ids := map[int]bool{} + for _, i := range r.Form["ids"] { + id, _ := strconv.Atoi(i) + user := inMemDB[id] + user.active = true + inMemDB[id] = user + ids[id] = true + } + tbody(inMemDB, ids).Render(r.Context(), w) +} + +func putDeactivate(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + log.Println(err) + w.WriteHeader(500) + return + } + ids := map[int]bool{} + for _, i := range r.Form["ids"] { + id, _ := strconv.Atoi(i) + user := inMemDB[id] + user.active = false + inMemDB[id] = user + ids[id] = true + } + tbody(inMemDB, ids).Render(r.Context(), w) +} diff --git a/bulkupdate/templates.templ b/bulkupdate/templates.templ new file mode 100644 index 0000000..fa9f324 --- /dev/null +++ b/bulkupdate/templates.templ @@ -0,0 +1,91 @@ +package bulkupdate + +import ( + "fmt" + + "examples/shared" +) + +templ demo(users []user) { + <h3 class="subtitle">Select Rows And Activate Or Deactivate Below</h3> + <form id="checked-contacts"> + <table class="table"> + <thead> + <tr> + <td></td> + <td>Name</td> + <td>Email</td> + <td>Status</td> + </tr> + </thead> + @tbody(users, map[int]bool{}) + </table> + </form> + <div hx-swap="outerHTML" hx-include="#checked-contacts" hx-target="#tbody" class="field is-grouped"> + <div class="control"><a class="button is-black" hx-put="/bulk-update/activate">Activate</a></div> + <div class="control"><a class="button" hx-put="/bulk-update/deactivate">Deactivate</a></div> + </div> + <style> + .htmx-settling tr.deactivate td { + background: lightcoral; + } + .htmx-settling tr.activate td { + background: darkseagreen; + } + tr td { + transition: all 1.2s; + } + </style> +} + +templ tbody(users []user, modified map[int]bool) { + <tbody id="tbody"> + for k, u := range users { + <tr + if modified[k] && u.active { + class="activate" + } + + if modified[k] && !u.active { + class="deactivate" + } + > + <td><input type="checkbox" name="ids" value={ fmt.Sprint(k) }/></td> + <td>{ u.name }</td> + <td>{ u.email }</td> + <td> + if u.active { + Active + } else { + Inactive + } + </td> + </tr> + } + </tbody> +} + +templ Index(users []user) { + @shared.Layout("Bulk Update") { + <h2 class="title">Bulk Update</h2> + <p>This demo shows how to implement a common pattern where rows are selected and then bulk updated. This is accomplished by putting a form around a table, with checkboxes in the table, and then including the checked values in <code>PUT</code>’s to two different endpoints: <code>activate</code>and <code>deactivate</code>:</p> + <pre><code class="language-html"> + @shared.Raw() { + @demo(users) + } +</code></pre> + <p>The server will either activate or deactivate the checked users and then rerender the <code>tbody</code>tag with updated rows. It will apply the class <code>activate</code>or <code>deactivate</code>to rows that have been mutated. This allows us to use a bit of CSS to flash a color helping the user see what happened:</p> + <pre><code class="language-css">{ `.htmx-settling tr.deactivate td { + background: lightcoral; +} +.htmx-settling tr.activate td { + background: darkseagreen; +} +tr td { + transition: all 1.2s; +}` }</code></pre> + <h2 class="title">Demo</h2> + @demo(users) + } +} + diff --git a/bulkupdate/templates_templ.go b/bulkupdate/templates_templ.go new file mode 100644 index 0000000..bf0d439 --- /dev/null +++ b/bulkupdate/templates_templ.go @@ -0,0 +1,853 @@ +// Code generated by templ@(devel) DO NOT EDIT. + +package bulkupdate + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +// GoExpression +import ( + "fmt" + + "examples/shared" +) + +func demo(users []user) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_1 := templ.GetChildren(ctx) + if var_1 == nil { + var_1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<h3") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"subtitle\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_2 := `Select Rows And Activate Or Deactivate Below` + _, err = templBuffer.WriteString(var_2) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h3>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<form") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" id=\"checked-contacts\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<table") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"table\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<thead>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<tr>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // Text + var_3 := `Name` + _, err = templBuffer.WriteString(var_3) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // Text + var_4 := `Email` + _, err = templBuffer.WriteString(var_4) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // Text + var_5 := `Status` + _, err = templBuffer.WriteString(var_5) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</tr>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</thead>") + if err != nil { + return err + } + // TemplElement + err = tbody(users, map[int]bool{}).Render(ctx, templBuffer) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</table>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</form>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-include=\"#checked-contacts\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-target=\"#tbody\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" class=\"field is-grouped\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<a") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"button is-black\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-put=\"/bulk-update/activate\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_6 := `Activate` + _, err = templBuffer.WriteString(var_6) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</a>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<a") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"button\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-put=\"/bulk-update/deactivate\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_7 := `Deactivate` + _, err = templBuffer.WriteString(var_7) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</a>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } +// RawElement + _, err = templBuffer.WriteString("<style>") + if err != nil { + return err + } +// Text +var_8 := ` + .htmx-settling tr.deactivate td { + background: lightcoral; + } + .htmx-settling tr.activate td { + background: darkseagreen; + } + tr td { + transition: all 1.2s; + } + ` +_, err = templBuffer.WriteString(var_8) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</style>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func tbody(users []user, modified map[int]bool) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_9 := templ.GetChildren(ctx) + if var_9 == nil { + var_9 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<tbody") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" id=\"tbody\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // For + for k, u := range users { + // Element (standard) + _, err = templBuffer.WriteString("<tr") + if err != nil { + return err + } + // Element Attributes + if modified[k] && u.active { + // Element Attributes + _, err = templBuffer.WriteString(" class=\"activate\"") + if err != nil { + return err + } + } + if modified[k] && !u.active { + // Element Attributes + _, err = templBuffer.WriteString(" class=\"deactivate\"") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<input") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" type=\"checkbox\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" name=\"ids\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" value=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(fmt.Sprint(k))) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // StringExpression + var var_10 string = u.name + _, err = templBuffer.WriteString(templ.EscapeString(var_10)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // StringExpression + var var_11 string = u.email + _, err = templBuffer.WriteString(templ.EscapeString(var_11)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // If + if u.active { + // Text + var_12 := `Active` + _, err = templBuffer.WriteString(var_12) + if err != nil { + return err + } + } else { + // Text + var_13 := `Inactive` + _, err = templBuffer.WriteString(var_13) + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</tr>") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("</tbody>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func Index(users []user) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_14 := templ.GetChildren(ctx) + if var_14 == nil { + var_14 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // TemplElement + var_15 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // Element (standard) + _, err = templBuffer.WriteString("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_16 := `Bulk Update` + _, err = templBuffer.WriteString(var_16) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h2>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<p>") + if err != nil { + return err + } + // Text + var_17 := `This demo shows how to implement a common pattern where rows are selected and then bulk updated. This is accomplished by putting a form around a table, with checkboxes in the table, and then including the checked values in ` + _, err = templBuffer.WriteString(var_17) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_18 := `PUT` + _, err = templBuffer.WriteString(var_18) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_19 := `’s to two different endpoints: ` + _, err = templBuffer.WriteString(var_19) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_20 := `activate` + _, err = templBuffer.WriteString(var_20) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_21 := `and ` + _, err = templBuffer.WriteString(var_21) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_22 := `deactivate` + _, err = templBuffer.WriteString(var_22) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_23 := `:` + _, err = templBuffer.WriteString(var_23) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</p>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<pre>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"language-html\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // TemplElement + var_24 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // TemplElement + err = demo(users).Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Raw().Render(templ.WithChildren(ctx, var_24), templBuffer) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</pre>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<p>") + if err != nil { + return err + } + // Text + var_25 := `The server will either activate or deactivate the checked users and then rerender the ` + _, err = templBuffer.WriteString(var_25) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_26 := `tbody` + _, err = templBuffer.WriteString(var_26) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_27 := `tag with updated rows. It will apply the class ` + _, err = templBuffer.WriteString(var_27) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_28 := `activate` + _, err = templBuffer.WriteString(var_28) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_29 := `or ` + _, err = templBuffer.WriteString(var_29) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_30 := `deactivate` + _, err = templBuffer.WriteString(var_30) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_31 := `to rows that have been mutated. This allows us to use a bit of CSS to flash a color helping the user see what happened:` + _, err = templBuffer.WriteString(var_31) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</p>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<pre>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"language-css\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // StringExpression + var var_32 string = `.htmx-settling tr.deactivate td { + background: lightcoral; +} +.htmx-settling tr.activate td { + background: darkseagreen; +} +tr td { + transition: all 1.2s; +}` + _, err = templBuffer.WriteString(templ.EscapeString(var_32)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</pre>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_33 := `Demo` + _, err = templBuffer.WriteString(var_33) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h2>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // TemplElement + err = demo(users).Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Layout("Bulk Update").Render(templ.WithChildren(ctx, var_15), templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + diff --git a/clicktoedit/handlers.go b/clicktoedit/handlers.go new file mode 100644 index 0000000..a9e7541 --- /dev/null +++ b/clicktoedit/handlers.go @@ -0,0 +1,53 @@ +package clicktoedit + +import ( + "log" + "net/http" +) + +type user struct { + firstName, lastName, email string +} + +var demoUser user = user{ + firstName: "Joe", + lastName: "Blow", + email: "joe@blow.com", +} + +func Handlers(prefix string, mux *http.ServeMux) { + mux.HandleFunc(prefix+"/", index) + mux.HandleFunc(prefix+"/contact/1", putUser) + mux.HandleFunc(prefix+"/contact/1/edit", editForm) +} + +func index(w http.ResponseWriter, r *http.Request) { + // Load user + user := demoUser + if r.Header.Get("HX-Request") == "true" { + Display(user).Render(r.Context(), w) + return + } + Index(user).Render(r.Context(), w) +} + +func editForm(w http.ResponseWriter, r *http.Request) { + // Load user + user := demoUser + Form(user).Render(r.Context(), w) +} + +func putUser(w http.ResponseWriter, r *http.Request) { + // Load user + if err := r.ParseForm(); err != nil { + log.Println(err) + w.WriteHeader(500) + return + } + demoUser = user{ + firstName: r.FormValue("firstName"), + lastName: r.FormValue("lastName"), + email: r.FormValue("email"), + } + Display(demoUser).Render(r.Context(), w) +} diff --git a/clicktoedit/templates.templ b/clicktoedit/templates.templ new file mode 100644 index 0000000..14d7b78 --- /dev/null +++ b/clicktoedit/templates.templ @@ -0,0 +1,60 @@ +package clicktoedit + +import "examples/shared" + +templ Display(u user) { + <div hx-target="this" hx-swap="outerHTML"> + <div><label>First Name</label>: { u.firstName }</div> + <div><label>Last Name</label>: { u.lastName }</div> + <div><label>Email</label>: { u.email }</div> + <button hx-get="/click-to-edit/contact/1/edit" class="button is-black">Click To Edit</button> + </div> +} + +templ Form(u user) { + <form hx-put="/click-to-edit/contact/1" hx-target="this" hx-swap="outerHTML"> + <div class="field"> + <div class="control"><label>First Name</label><input class="input" type="text" name="firstName" value={ u.firstName }/></div> + </div> + <div class="field"> + <div class="control"><label>Last Name</label><input class="input" type="text" name="lastName" value={ u.lastName }/></div> + </div> + <div class="field"> + <div class="control"><label>Email Address</label><input class="input" type="email" name="email" value={ u.email }/></div> + </div> + <div class="field is-grouped"> + <div class="control"><button class="button is-black">Submit</button></div> + <div class="control"><button class="button" hx-get="/click-to-edit/contact/1">Cancel</button></div> + </div> + </form> +} + +templ Index(u user) { + @shared.Layout("Click to Edit") { + <h2 class="title">Click to Edit</h2> + <p>The click to edit pattern provides a way to offer inline editing of all or part of a record without a page refresh.</p> + <ul> + <li> + This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from + <code>/contacts/1/edit</code> + <pre><code class="language-html"> + @shared.Raw() { + @Display(u) + } +</code></pre> + </li> + <li> + This returns a form that can be used to edit the contact + <pre><code class="language-html"> + @shared.Raw() { + @Form(u) + } +</code></pre> + </li> + <li>The form issues a <code>PUT</code>back to <code>/contacts/1</code>, following the usual REST-ful pattern.</li> + </ul> + <h2 class="title">Demo</h2> + @Display(u) + } +} + diff --git a/clicktoedit/templates_templ.go b/clicktoedit/templates_templ.go new file mode 100644 index 0000000..e024433 --- /dev/null +++ b/clicktoedit/templates_templ.go @@ -0,0 +1,952 @@ +// Code generated by templ@(devel) DO NOT EDIT. + +package clicktoedit + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +// GoExpression +import "examples/shared" + +func Display(u user) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_1 := templ.GetChildren(ctx) + if var_1 == nil { + var_1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-target=\"this\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_2 := `First Name` + _, err = templBuffer.WriteString(var_2) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Text + var_3 := `: ` + _, err = templBuffer.WriteString(var_3) + if err != nil { + return err + } + // StringExpression + var var_4 string = u.firstName + _, err = templBuffer.WriteString(templ.EscapeString(var_4)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_5 := `Last Name` + _, err = templBuffer.WriteString(var_5) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Text + var_6 := `: ` + _, err = templBuffer.WriteString(var_6) + if err != nil { + return err + } + // StringExpression + var var_7 string = u.lastName + _, err = templBuffer.WriteString(templ.EscapeString(var_7)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_8 := `Email` + _, err = templBuffer.WriteString(var_8) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Text + var_9 := `: ` + _, err = templBuffer.WriteString(var_9) + if err != nil { + return err + } + // StringExpression + var var_10 string = u.email + _, err = templBuffer.WriteString(templ.EscapeString(var_10)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<button") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-get=\"/click-to-edit/contact/1/edit\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" class=\"button is-black\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_11 := `Click To Edit` + _, err = templBuffer.WriteString(var_11) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</button>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func Form(u user) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_12 := templ.GetChildren(ctx) + if var_12 == nil { + var_12 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<form") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-put=\"/click-to-edit/contact/1\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-target=\"this\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"field\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_13 := `First Name` + _, err = templBuffer.WriteString(var_13) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<input") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"input\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" type=\"text\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" name=\"firstName\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" value=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(u.firstName)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"field\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_14 := `Last Name` + _, err = templBuffer.WriteString(var_14) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<input") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"input\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" type=\"text\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" name=\"lastName\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" value=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(u.lastName)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"field\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label>") + if err != nil { + return err + } + // Text + var_15 := `Email Address` + _, err = templBuffer.WriteString(var_15) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</label>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<input") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"input\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" type=\"email\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" name=\"email\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" value=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(u.email)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"field is-grouped\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<button") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"button is-black\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_16 := `Submit` + _, err = templBuffer.WriteString(var_16) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</button>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<button") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"button\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-get=\"/click-to-edit/contact/1\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_17 := `Cancel` + _, err = templBuffer.WriteString(var_17) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</button>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</form>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func Index(u user) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_18 := templ.GetChildren(ctx) + if var_18 == nil { + var_18 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // TemplElement + var_19 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // Element (standard) + _, err = templBuffer.WriteString("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_20 := `Click to Edit` + _, err = templBuffer.WriteString(var_20) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h2>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<p>") + if err != nil { + return err + } + // Text + var_21 := `The click to edit pattern provides a way to offer inline editing of all or part of a record without a page refresh.` + _, err = templBuffer.WriteString(var_21) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</p>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<ul>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<li>") + if err != nil { + return err + } + // Text + var_22 := `This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from ` + _, err = templBuffer.WriteString(var_22) + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_23 := `/contacts/1/edit` + _, err = templBuffer.WriteString(var_23) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<pre>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"language-html\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // TemplElement + var_24 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // TemplElement + err = Display(u).Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Raw().Render(templ.WithChildren(ctx, var_24), templBuffer) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</pre>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</li>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<li>") + if err != nil { + return err + } + // Text + var_25 := `This returns a form that can be used to edit the contact` + _, err = templBuffer.WriteString(var_25) + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<pre>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"language-html\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // TemplElement + var_26 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // TemplElement + err = Form(u).Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Raw().Render(templ.WithChildren(ctx, var_26), templBuffer) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</pre>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</li>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<li>") + if err != nil { + return err + } + // Text + var_27 := `The form issues a ` + _, err = templBuffer.WriteString(var_27) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_28 := `PUT` + _, err = templBuffer.WriteString(var_28) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_29 := `back to ` + _, err = templBuffer.WriteString(var_29) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<code>") + if err != nil { + return err + } + // Text + var_30 := `/contacts/1` + _, err = templBuffer.WriteString(var_30) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</code>") + if err != nil { + return err + } + // Text + var_31 := `, following the usual REST-ful pattern.` + _, err = templBuffer.WriteString(var_31) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</li>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</ul>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_32 := `Demo` + _, err = templBuffer.WriteString(var_32) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h2>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // TemplElement + err = Display(u).Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Layout("Click to Edit").Render(templ.WithChildren(ctx, var_19), templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8e2c193 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module examples + +go 1.21 + +require github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 + +require ( + github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 // indirect + golang.org/x/net v0.9.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..482aab9 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/a-h/templ v0.2.233 h1:EnZqZmtV0YICqWG6MtLNmTcWuFkl2ImyQ63SIpWaM2Y= +github.com/a-h/templ v0.2.233/go.mod h1:h1DdzFMWVApvTcZBNmM6+mD6EPq6uYkncMNF7zdLj9I= +github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 h1:NeF/iw7KU9W7CYYJimd5x7ooOXCLrfo8FcHdFPUU+2w= +github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5/go.mod h1:nqma2qb9ViAJOP4MBucyH+SPbOyNDZaRQyusfpK4PjY= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4/go.mod h1:+ccdNT0xMY1dtc5XBxumbYfOUhmduiGudqaDgD2rVRE= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= diff --git a/home.templ b/home.templ new file mode 100644 index 0000000..f90a478 --- /dev/null +++ b/home.templ @@ -0,0 +1,23 @@ +package main + +import "examples/shared" + +templ Home(examples []Example) { + @shared.Layout("Home") { + <h2 class="title">Examples:</h2> + <table class="table is-fullwidth"><thead> + <tr> + <th>Pattern</th> + <th>Description</th> + </tr> +</thead><tbody> + for _, e := range examples { + <tr> + <td><a href={ templ.SafeURL("/"+e.Slug) }>{ e.Name }</a></td> + <td>{ e.Desc }</td> + </tr> + } +</tbody></table> + } +} + diff --git a/home_templ.go b/home_templ.go new file mode 100644 index 0000000..715da7f --- /dev/null +++ b/home_templ.go @@ -0,0 +1,227 @@ +// Code generated by templ@(devel) DO NOT EDIT. + +package main + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +// GoExpression +import "examples/shared" + +func Home(examples []Example) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_1 := templ.GetChildren(ctx) + if var_1 == nil { + var_1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // TemplElement + var_2 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // Element (standard) + _, err = templBuffer.WriteString("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_3 := `Examples:` + _, err = templBuffer.WriteString(var_3) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</h2>") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<table") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"table is-fullwidth\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<thead>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<tr>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<th>") + if err != nil { + return err + } + // Text + var_4 := `Pattern` + _, err = templBuffer.WriteString(var_4) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</th>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<th>") + if err != nil { + return err + } + // Text + var_5 := `Description` + _, err = templBuffer.WriteString(var_5) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</th>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</tr>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</thead>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<tbody>") + if err != nil { + return err + } + // For + for _, e := range examples { + // Element (standard) + _, err = templBuffer.WriteString("<tr>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<a") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" href=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + var var_6 templ.SafeURL = templ.SafeURL("/"+e.Slug) + _, err = templBuffer.WriteString(templ.EscapeString(string(var_6))) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // StringExpression + var var_7 string = e.Name + _, err = templBuffer.WriteString(templ.EscapeString(var_7)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</a>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<td>") + if err != nil { + return err + } + // StringExpression + var var_8 string = e.Desc + _, err = templBuffer.WriteString(templ.EscapeString(var_8)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</td>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</tr>") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("</tbody>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</table>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Layout("Home").Render(templ.WithChildren(ctx, var_2), templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + diff --git a/main.go b/main.go new file mode 100644 index 0000000..aaccedc --- /dev/null +++ b/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "examples/bulkupdate" + "examples/clicktoedit" + "log" + "net/http" + + "github.com/a-h/templ" +) + +func main() { + if err := run(); err != nil { + log.Fatal(err) + } +} + +func run() error { + r := http.NewServeMux() + r.Handle("/", templ.Handler(Home(examples))) + for _, e := range examples { + log.Printf("Serving %q on /%s", e.Name, e.Slug) + e.Handlers("/"+e.Slug, r) + } + log.Println("Listening on localhost:2468") + return http.ListenAndServe("localhost:2468", r) +} + +type Example struct { + Name, Desc, Slug string + Handlers func(string, *http.ServeMux) +} + +var examples = []Example{ + { + Name: "Click To Edit", + Desc: "Demonstrates inline editing of a data object", + Slug: "click-to-edit", + Handlers: clicktoedit.Handlers, + }, + { + Name: "Bulk Update", + Desc: "Demonstrates bulk updating of multiple rows of data", + Slug: "bulk-update", + Handlers: bulkupdate.Handlers, + }, +} diff --git a/shared/layout.templ b/shared/layout.templ new file mode 100644 index 0000000..db3a5a0 --- /dev/null +++ b/shared/layout.templ @@ -0,0 +1,50 @@ +package shared + +import "os" + +templ Layout(title string) { + <html> + <head> + <link href="https://unpkg.com/prismjs@1.29.0/themes/prism-twilight.css" rel="stylesheet"/> + <style> + .token.number, + .token.tag { + all: inherit; + color: hsl(14, 58%, 55%); + } + </style> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"/> + <title>Go htmx examples - { title }</title> + <script src="https://unpkg.com/htmx.org@1.8.5"></script> + if os.Getenv("DEBUG") == "true" { + <style> + @keyframes debug { + from {outline: 3px solid #FF0000FF;} + to {outline: 3px solid #FF000000;} + } + * { + animation-name: debug; + animation-duration: 1s; + } + </style> + } + </head> + <body hx-boost="true"> + @Nav() + <div id="content" class="container p-5 content"> + { children... } + </div> + <script src="https://unpkg.com/prismjs@1.29.0/components/prism-core.min.js"></script> + <script src="https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script> + </body> + </html> +} + +templ Nav() { + <nav class="navbar is-black has-shadow" role="navigation" aria-label="main navigation"> + <div class="navbar-brand"> + <div class="navbar-start"><a class="navbar-item has-text-weight-bold is-size-3" href="/">Go HTMX Examples</a></div> + </div> + </nav> +} + diff --git a/shared/layout_templ.go b/shared/layout_templ.go new file mode 100644 index 0000000..ca0981c --- /dev/null +++ b/shared/layout_templ.go @@ -0,0 +1,389 @@ +// Code generated by templ@(devel) DO NOT EDIT. + +package shared + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +// GoExpression +import "os" + +func Layout(title string) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_1 := templ.GetChildren(ctx) + if var_1 == nil { + var_1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<html>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<head>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<link") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" href=\"https://unpkg.com/prismjs@1.29.0/themes/prism-twilight.css\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" rel=\"stylesheet\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } +// RawElement + _, err = templBuffer.WriteString("<style>") + if err != nil { + return err + } +// Text +var_2 := ` + .token.number, + .token.tag { + all: inherit; + color: hsl(14, 58%, 55%); + } + ` +_, err = templBuffer.WriteString(var_2) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</style>") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<link") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" rel=\"stylesheet\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" href=\"https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<title>") + if err != nil { + return err + } + // Text + var_3 := `Go htmx examples - ` + _, err = templBuffer.WriteString(var_3) + if err != nil { + return err + } + // StringExpression + var var_4 string = title + _, err = templBuffer.WriteString(templ.EscapeString(var_4)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</title>") + if err != nil { + return err + } +// RawElement + _, err = templBuffer.WriteString("<script") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" src=\"https://unpkg.com/htmx.org@1.8.5\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } +// Text +var_5 := `` +_, err = templBuffer.WriteString(var_5) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</script>") + if err != nil { + return err + } + // If + if os.Getenv("DEBUG") == "true" { +// RawElement + _, err = templBuffer.WriteString("<style>") + if err != nil { + return err + } +// Text +var_6 := ` + @keyframes debug { + from {outline: 3px solid #FF0000FF;} + to {outline: 3px solid #FF000000;} + } + * { + animation-name: debug; + animation-duration: 1s; + } + ` +_, err = templBuffer.WriteString(var_6) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</style>") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("</head>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<body") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-boost=\"true\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // TemplElement + err = Nav().Render(ctx, templBuffer) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" id=\"content\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" class=\"container p-5 content\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Children + err = var_1.Render(ctx, templBuffer) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } +// RawElement + _, err = templBuffer.WriteString("<script") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" src=\"https://unpkg.com/prismjs@1.29.0/components/prism-core.min.js\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } +// Text +var_7 := `` +_, err = templBuffer.WriteString(var_7) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</script>") + if err != nil { + return err + } +// RawElement + _, err = templBuffer.WriteString("<script") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" src=\"https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } +// Text +var_8 := `` +_, err = templBuffer.WriteString(var_8) +if err != nil { + return err +} + _, err = templBuffer.WriteString("</script>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</body>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</html>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func Nav() templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_9 := templ.GetChildren(ctx) + if var_9 == nil { + var_9 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("<nav") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"navbar is-black has-shadow\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" role=\"navigation\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" aria-label=\"main navigation\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"navbar-brand\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"navbar-start\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<a") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"navbar-item has-text-weight-bold is-size-3\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" href=\"/\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Text + var_10 := `Go HTMX Examples` + _, err = templBuffer.WriteString(var_10) + if err != nil { + return err + } + _, err = templBuffer.WriteString("</a>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</nav>") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + diff --git a/shared/shared.go b/shared/shared.go new file mode 100644 index 0000000..4453ede --- /dev/null +++ b/shared/shared.go @@ -0,0 +1,25 @@ +package shared + +import ( + "bytes" + "context" + "io" + + "github.com/a-h/templ" + "github.com/yosssi/gohtml" +) + +func Raw() templ.ComponentFunc { + return func(ctx context.Context, w io.Writer) error { + b := new(bytes.Buffer) + if err := templ.GetChildren(ctx).Render(ctx, b); err != nil { + return err + } + gohtml.Condense = true + str := templ.EscapeString(gohtml.Format(b.String())) + if _, err := w.Write([]byte(str)); err != nil { + return err + } + return nil + } +}