add playground
This commit is contained in:
parent
465bec9b6d
commit
d954007656
5 changed files with 58 additions and 272 deletions
|
|
@ -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 >}}
|
||||
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ↗
|
||||
</button>
|
||||
<button role="button" class="hugo-goplay-button" onclick={{ safeJS (print "goplayOpenShare_" $id "()") }}>Share ↗</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>
|
||||
|
|
@ -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 ↗
|
||||
</button>
|
||||
<button role="button" class="hugo-goplay-button" onclick={{ safeJS (print "goplayOpenShare_" $id "()") }}>Share ↗</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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue