add gpfield

This commit is contained in:
Simon Vieille 2025-07-28 15:51:14 +02:00
commit fddd0643de
Signed by: deblan
GPG key ID: 579388D585F70417
3 changed files with 213 additions and 470 deletions

View file

@ -11,36 +11,10 @@ A field represents a field in a form.
```golang
func NewFieldCheckbox(name string) *Field
```
{{% goplay %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldCheckbox("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
</pre>
{{% /goplay %}}
{{% gpfield %}}
<pre class="hidden">form.NewFieldCheckbox("Foo")</pre>
{{% /gpfield %}}
Generates an input[type=checkbox]
@ -50,36 +24,11 @@ Generates an input[type=checkbox]
func NewFieldChoice(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldChoice("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldChoice("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates inputs (checkbox or radio) or selects
@ -90,36 +39,13 @@ Generates inputs (checkbox or radio) or selects
func NewFieldCsrf(name string) *Field
```
{{% goplay %}}
{{% details title="Test me" closed="true" %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldCsrf("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldCsrf("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
{{% /details %}}
### Date
@ -127,36 +53,11 @@ func r(f *form.Form) {
func NewFieldDate(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldDate("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldDate("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=date] with default transformers
@ -166,36 +67,11 @@ Generates an input[type=date] with default transformers
func NewFieldDatetime(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldDatetime("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldDatetime("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=datetime] with default transformers
@ -205,36 +81,11 @@ Generates an input[type=datetime] with default transformers
func NewFieldDatetimeLocal(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldDatetimeLocal("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldDatetimeLocal("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=datetime-local] with default transformers
@ -244,36 +95,11 @@ Generates an input[type=datetime-local] with default transformers
func NewFieldHidden(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldHidden("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldHidden("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=hidden]
@ -283,36 +109,11 @@ Generates an input[type=hidden]
func NewFieldMail(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldMail("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldMail("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=email]
@ -322,36 +123,11 @@ Generates an input[type=email]
func NewFieldNumber(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldNumber("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldNumber("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=number] with default transformers
@ -361,36 +137,11 @@ Generates an input[type=number] with default transformers
func NewFieldPassword(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldPassword("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldPassword("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=password]
@ -400,36 +151,11 @@ Generates an input[type=password]
func NewFieldRange(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldRange("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldRange("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=range]
@ -439,36 +165,11 @@ Generates an input[type=range]
func NewFieldSubForm(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldSubForm("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldSubForm("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Alias:
@ -484,36 +185,11 @@ Generates a sub form
func NewFieldText(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldText("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldText("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=text]
@ -523,36 +199,11 @@ Generates an input[type=text]
func NewFieldTextarea(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldTextarea("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldTextarea("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates a textarea
@ -562,36 +213,11 @@ Generates a textarea
func NewFieldTime(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewFieldTime("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewFieldTime("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=time] with default transformers
@ -601,36 +227,11 @@ Generates an input[type=time] with default transformers
func NewSubmit(name string) *Field
```
{{% goplay %}}
{{% gpfield %}}
<pre class="hidden">
package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
field := form.NewSubmit("Foo")
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget (.Form.GetField \"Foo\") }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
field := form.NewSubmit("Foo")
</pre>
{{% /goplay %}}
{{% /gpfield %}}
Generates an input[type=submit]

View file

@ -8,27 +8,30 @@ weight: 6
Here is a simple example that displays a form:
```golang
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
myForm := form.NewForm(...)
myForm := form.NewForm(...)
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
<html>
<head>
<title>My form</title>
</head>
<body>
{{ form .Form }}
</body>
</html>
`)
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
<html>
<head>
<title>My form</title>
</head>
<body>
{{ form .Form }}
</body>
</html>
`)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
tpl.Execute(w, map[string]any{
"Form": myForm,
})
tpl.Execute(w, map[string]any{
"Form": myForm,
})
}
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("{{ form_widget .Form }}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
```
Other helper functions are available to render specific parts of the form:

View file

@ -0,0 +1,139 @@
{{ $id := md5 .Inner }} {{ .Inner }}
<details>
<summary class="hugo-goplay-summary">Test me</summary>
<textarea class="hugo-goplay-textarea" id="pre-{{ $id }}"></textarea>
<div id="{{ $id }}" class="hugo-goplay-result"></div>
<div class="hugo-goplay-toolbox">
<button role="button" class="hugo-goplay-button" onclick={{ safeJS (print "goplayRenderCompile_" $id "()") }}>Run</button>
<button role="button" class="hugo-goplay-button" onclick={{ safeJS (print "goplayOpenShare_" $id "()") }}>
Try it yourself &#8599;
</button>
<button role="button" class="hugo-goplay-button" onclick={{ safeJS (print "goplayOpenShare_" $id "()") }}>Share &#8599;</button>
</div>
</details>
<style>
.hidden {
display: none;
}
.hugo-goplay-summary {
cursor: pointer;
}
.hugo-goplay-textarea {
padding: 1rem;
width: 100%;
border-radius: 4px;
margin: 10px 0;
background-color: hsl(var(--primary-hue) var(--primary-saturation) calc(var(--primary-lightness) + calc(calc(100% - var(--primary-lightness)) / 50) * 27) / 0.1);
font-family: monospace;
}
.hugo-goplay-result pre {
padding: 1rem;
}
.hugo-goplay-result .system {
color: green;
}
.hugo-goplay-result .stderr {
color: red;
}
.hugo-goplay-toolbox {
display: flex;
justify-content: flex-start;
margin-bottom: 2rem;
}
.hugo-goplay-toolbox .hugo-goplay-button {
font-size: 13px;
font-weight: bold;
border: 1px solid var(--primary);
padding: 0.15rem 0.75rem;
border-radius: 4px;
margin-left: 0.5rem;
color: var(--primary);
background-color: var(--theme);
}
.hugo-goplay-toolbox .hugo-goplay-button:hover {
border: 1px solid var(--theme);
color: var(--theme);
background-color: var(--primary);
}
.hugo-goplay-toolbox .hugo-goplay-button:first-child {
margin-left: 0;
}
</style>
<script type="module">
import { GoPlayProxy } from "https://unpkg.com/@ggicci/goplay/dist/index.js";
const goplay = new GoPlayProxy("https://gp.deblan.gitnet.page");
function normalizeCode(code) {
code = code
.trim()
.replace(/^```go/, "")
.replace(/^```/, "")
.replace(/```$/, "")
.replace(/<([^>]+)>/g, "")
.trim();
let lines = code.split("\n");
for (let i in lines) {
lines[i] = [" ", lines[i]].join("")
}
code = lines.join("\n");
code = `package main
import (
"fmt"
"html/template"
"strings"
"gitnet.fr/deblan/go-form/form"
"gitnet.fr/deblan/go-form/theme"
)
func main() {
${code}
r(form.NewForm(field))
}
func r(f *form.Form) {
render := theme.NewRenderer(theme.Html5)
tpl, _ := template.New("example").Funcs(render.FuncMap()).Parse("\{\{ form_widget (.Form.GetField \"Foo\") \}\}")
b := new(strings.Builder)
tpl.Execute(b, map[string]any{"Form": f})
fmt.Println(b.String())
}
`;
return code
}
const normalizedCode = normalizeCode("{{ .Inner }}")
const textarea = document.getElementById("pre-{{ $id }}")
textarea.value = normalizedCode
textarea.style.height = textarea.scrollHeight + "px";
textarea.style.overflowY = "hidden";
textarea.addEventListener("input", function() {
this.style.height = "auto";
this.style.height = this.scrollHeight + "px";
});
window["goplayRenderCompile_" + "{{ $id }}"] = () => {
const parent = document.getElementById("{{ $id }}")
const pre = document.createElement('pre')
const container = document.createElement('code')
container.classList.add('text')
pre.appendChild(container)
parent.replaceChildren(pre)
goplay.renderCompile(container, textarea.value);
};
window["goplayOpenShare_" + "{{ $id }}"] = async() => {
const shareUrl = await goplay.share(normalizedCode)
window.open(shareUrl, "_blank").focus();
};
</script>