add playground

This commit is contained in:
Simon Vieille 2025-07-28 18:13:09 +02:00
commit d954007656
Signed by: deblan
GPG key ID: 579388D585F70417
5 changed files with 58 additions and 272 deletions

View file

@ -12,15 +12,3 @@ Creating and processing HTML forms is hard and repetitive. You need to deal with
* Data mounting to populate a form from a struct instance
* Data binding to populate a struct instance from a submitted form
* Form renderer with customizable themes
<!--
{{< callout emoji="❓" >}}
Hextra is still in active development.
Have a question or feedback? Feel free to [open an issue](https://github.com/imfing/hextra/issues)!
{{< /callout >}}
{{< cards >}}
{{< card link="getting-started" title="Getting Started" icon="document-text" subtitle="Learn how to create website using Hextra" >}}
{{< /cards >}}
-->

View file

@ -12,9 +12,9 @@ A field represents a field in a form.
func NewFieldCheckbox(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">field := form.NewFieldCheckbox("Foo")</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=checkbox]
@ -24,11 +24,11 @@ Generates an input[type=checkbox]
func NewFieldChoice(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldChoice("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates inputs (checkbox or radio) or selects
@ -39,11 +39,11 @@ Generates inputs (checkbox or radio) or selects
func NewFieldCsrf(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldCsrf("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
### Date
@ -51,11 +51,11 @@ field := form.NewFieldCsrf("Foo")
func NewFieldDate(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldDate("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=date] with default transformers
@ -65,11 +65,11 @@ Generates an input[type=date] with default transformers
func NewFieldDatetime(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldDatetime("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=datetime] with default transformers
@ -79,11 +79,11 @@ Generates an input[type=datetime] with default transformers
func NewFieldDatetimeLocal(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldDatetimeLocal("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=datetime-local] with default transformers
@ -93,11 +93,11 @@ Generates an input[type=datetime-local] with default transformers
func NewFieldHidden(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldHidden("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=hidden]
@ -107,11 +107,11 @@ Generates an input[type=hidden]
func NewFieldMail(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldMail("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=email]
@ -121,11 +121,11 @@ Generates an input[type=email]
func NewFieldNumber(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldNumber("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=number] with default transformers
@ -135,11 +135,11 @@ Generates an input[type=number] with default transformers
func NewFieldPassword(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldPassword("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=password]
@ -149,11 +149,11 @@ Generates an input[type=password]
func NewFieldRange(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldRange("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=range]
@ -163,11 +163,11 @@ Generates an input[type=range]
func NewFieldSubForm(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldSubForm("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Alias:
@ -183,11 +183,11 @@ Generates a sub form
func NewFieldText(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldText("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=text]
@ -197,11 +197,11 @@ Generates an input[type=text]
func NewFieldTextarea(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldTextarea("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates a textarea
@ -211,11 +211,11 @@ Generates a textarea
func NewFieldTime(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewFieldTime("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=time] with default transformers
@ -225,11 +225,11 @@ Generates an input[type=time] with default transformers
func NewSubmit(name string) *Field
```
{{% gpfield %}}
{{% goplay-field %}}
<pre class="hidden">
field := form.NewSubmit("Foo")
</pre>
{{% /gpfield %}}
{{% /goplay-field %}}
Generates an input[type=submit]

View file

@ -10,7 +10,8 @@ Here is a simple example that displays a form:
```golang
myForm := form.NewForm(...)
render := theme.NewRenderer(theme.Html5)
render := theme.NewRenderer(theme.Bootstrap5)
tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
<html>
<head>
@ -22,18 +23,39 @@ tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
</html>
`)
b := new(strings.Builder)
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())
```
{{% goplay-auto-import-main %}}
<pre class="hidden">
@import "html/template"
@import "net/http"
@import
@import "gitnet.fr/deblan/go-form/example"
@import "gitnet.fr/deblan/go-form/form"
@import "gitnet.fr/deblan/go-form/theme"
form := example.CreateDataForm()
render := theme.NewRenderer(theme.Bootstrap5)
tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`{{ form .Form }}`)
b := new(strings.Builder)
tpl.Execute(w, map[string]any{
"Form": myForm,
})
fmt.Println(b.String())
</pre>
{{% /goplay-auto-import-main %}}
Other helper functions are available to render specific parts of the form:
- `form_errors`: displays the form's global errors

View file

@ -1,81 +0,0 @@
{{ $id := md5 .Inner }} {{ .Inner }}
<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>
<style>
.hidden {
display: none;
}
.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) {
return code
.trim()
.replace(/^```go/, "")
.replace(/^```/, "")
.replace(/```$/, "")
.replace(/<.*pre.*>/g, "")
.trim();
}
const normalizedCode = normalizeCode("{{ .Inner }}")
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,normalizedCode);
};
window["goplayOpenShare_" + "{{ $id }}"] = async() => {
const shareUrl = await goplay.share(normalizedCode)
window.open(shareUrl, "_blank").focus();
};
</script>

View file

@ -1,143 +0,0 @@
{{ $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;
}
#{{ $id }} code {
background: none !important;
border: 0 !important;
}
.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>