feat: replace templates with components
This commit is contained in:
parent
e6c2bfad36
commit
960d9175b1
8 changed files with 572 additions and 400 deletions
|
|
@ -37,7 +37,7 @@ func DateBeforeMount(data any, format string) (any, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Generates an input[type=date] with default transformers
|
||||
|
|
|
|||
|
|
@ -26,3 +26,15 @@ func NewOption(name string, value any) *Option {
|
|||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Option) AsBool() bool {
|
||||
return o.Value.(bool)
|
||||
}
|
||||
|
||||
func (o *Option) AsString() string {
|
||||
return o.Value.(string)
|
||||
}
|
||||
|
||||
func (o *Option) AsMapString() map[string]string {
|
||||
return o.Value.(map[string]string)
|
||||
}
|
||||
|
|
|
|||
6
go.mod
6
go.mod
|
|
@ -7,3 +7,9 @@ require (
|
|||
github.com/spf13/cast v1.9.2
|
||||
github.com/yassinebenaid/godump v0.11.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
maragu.dev/gomponents v1.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -10,7 +10,13 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
|||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI=
|
||||
github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
maragu.dev/gomponents v1.1.0 h1:iCybZZChHr1eSlvkWp/JP3CrZGzctLudQ/JI3sBcO4U=
|
||||
maragu.dev/gomponents v1.1.0/go.mod h1:oEDahza2gZoXDoDHhw8jBNgH+3UR5ni7Ur648HORydM=
|
||||
|
|
|
|||
|
|
@ -15,141 +15,147 @@ package theme
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var Bootstrap5 = map[string]string{
|
||||
"form": `<form action="{{ .Form.Action }}" method="{{ .Form.Method }}" {{ form_attr .Form }}>
|
||||
{{- form_error .Form nil -}}
|
||||
var Bootstrap5 = ExtendTheme(Html5, func() map[string]RenderFunc {
|
||||
theme := make(map[string]RenderFunc)
|
||||
|
||||
{{- form_help .Form -}}
|
||||
return theme
|
||||
})
|
||||
|
||||
{{- range $field := .Form.Fields -}}
|
||||
{{- form_row $field -}}
|
||||
{{- end -}}
|
||||
</form>`,
|
||||
"attributes": `{{ range $key, $value := .Attributes }}{{ $key }}="{{ $value }}"{{ end }}`,
|
||||
"help": `
|
||||
{{- if gt (len .Help) 0 -}}
|
||||
<div class="form-help">{{ .Help }}</div>
|
||||
{{- end -}}
|
||||
`,
|
||||
"label": `
|
||||
{{ if .Field.HasOption "label" }}
|
||||
{{ $label := (.Field.GetOption "label").Value }}
|
||||
|
||||
{{- if ne $label "" -}}
|
||||
<label for="{{ .Field.GetId }}" {{ form_label_attr .Field }} class="form-label">{{ $label }}</label>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
`,
|
||||
"input": `
|
||||
{{- $type := .Field.GetOption "type" -}}
|
||||
{{- $checked := and (eq (.Field.GetOption "type").Value "checkbox") (.Field.Data) -}}
|
||||
{{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
{{- $value := .Field.Data -}}
|
||||
{{- $class := "form-control" }}
|
||||
|
||||
{{- if eq $type.Value "checkbox" -}}
|
||||
{{- $value = 1 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if or (eq $type.Value "checkbox") (eq $type.Value "radio") -}}
|
||||
{{- $class = "form-check-input" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if eq $type.Value "range" -}}
|
||||
{{- $class = "form-range" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if or (eq $type.Value "submit") (eq $type.Value "reset") (eq $type.Value "button") -}}
|
||||
{{- $class = "" -}}
|
||||
|
||||
{{ if .Field.HasOption "attr" }}
|
||||
{{ $class = (.Field.GetOption "attr").Value.attr.class }}
|
||||
{{ end }}
|
||||
{{- end -}}
|
||||
|
||||
<input id="{{ .Field.GetId }}" {{ if $checked }}checked{{ end }} {{ if $required }}required="required"{{ end }} name="{{ .Field.GetName }}" value="{{ $value }}" type="{{ $type.Value }}" {{ form_widget_attr .Field }} class="{{ $class }}">
|
||||
`,
|
||||
"textarea": `
|
||||
<textarea id="{{ .Field.GetId }}" {{ if .Field.HasOption "required" }}{{ if (.Field.GetOption "required").Value }}required="required"{{ end }}{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }} class="form-control">{{ .Field.Data }}</textarea>
|
||||
`,
|
||||
"choice": `
|
||||
{{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
{{- $isExpanded := (.Field.GetOption "expanded").Value -}}
|
||||
{{- $isMultiple := (.Field.GetOption "multiple").Value -}}
|
||||
{{- $emptyChoiceLabel := (.Field.GetOption "empty_choice_label").Value -}}
|
||||
{{- $choices := (.Field.GetOption "choices").Value -}}
|
||||
{{- $field := .Field -}}
|
||||
{{- $keyAdd := 0 -}}
|
||||
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
{{- $keyAdd = 1 -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $isExpanded -}}
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
<div class="form-check">
|
||||
<input value="" {{ if not $field.Data }}checked{{ end }} name="{{ $field.GetName }}" type="radio" id="{{ $field.GetId }}-0" class="form-check-input">
|
||||
<label for="{{ $field.GetId }}-0" class="form-check-label">{{ ($field.GetOption "empty_choice_label").Value }}</label>
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
{{- range $key, $choice := $choices.GetChoices -}}
|
||||
<div class="form-check">
|
||||
<input name="{{ $field.GetName }}" type="{{ if $isMultiple }}checkbox{{ else }}radio{{ end }}" value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}checked{{ end }} id="{{ $field.GetId }}-{{ sum $key $keyAdd }}" class="form-check-input">
|
||||
<label for="{{ $field.GetId }}-{{ sum $key $keyAdd }}" class="form-check-label">{{- $choice.Label -}}</label>
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
<select id="{{ .Field.GetId }}" {{ if $required }}required="required"{{ end }} {{ if $isMultiple }}multiple{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }} class="form-select">
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
<option value="">{{ $emptyChoiceLabel }}</option>
|
||||
{{- end -}}
|
||||
{{- range $choice := $choices.GetChoices -}}
|
||||
<option value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}selected{{ end }}>{{ $choice.Label }}</option>
|
||||
{{- end -}}
|
||||
</select>
|
||||
{{- end -}}
|
||||
`,
|
||||
"sub_form": `
|
||||
<fieldset id="{{ .Field.GetId }}">
|
||||
{{ if .Field.HasOption "label" }}
|
||||
{{ $label := (.Field.GetOption "label").Value }}
|
||||
|
||||
{{- if ne $label "" -}}
|
||||
<legend {{ form_label_attr .Field }}>{{ $label }}</legend>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{ form_widget_help .Field }}
|
||||
|
||||
{{- range $field := .Field.Children -}}
|
||||
{{- form_row $field -}}
|
||||
{{- end -}}
|
||||
</fieldset>
|
||||
`,
|
||||
"error": `
|
||||
{{- if gt (len .Errors) 0 -}}
|
||||
<div class="invalid-feedback d-block">
|
||||
{{- range $error := .Errors -}}
|
||||
<div>{{- $error -}}</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
`,
|
||||
"row": `<div {{ form_row_attr .Field }}>
|
||||
{{ $labelAfterWidget := and (.Field.HasOption "type") (eq (.Field.GetOption "type").Value "checkbox") }}
|
||||
|
||||
{{ if and (eq (len .Field.Children) 0) (not $labelAfterWidget) }}
|
||||
{{- form_label .Field -}}
|
||||
{{ end }}
|
||||
|
||||
{{- form_widget .Field -}}
|
||||
{{- form_error nil .Field -}}
|
||||
|
||||
{{ if and (eq (len .Field.Children) 0) ($labelAfterWidget) }}
|
||||
{{- form_label .Field -}}
|
||||
{{ end }}
|
||||
|
||||
{{- form_widget_help .Field -}}
|
||||
</div>`,
|
||||
}
|
||||
// var Bootstrap5 = map[string]string{
|
||||
// "form": `<form action="{{ .Form.Action }}" method="{{ .Form.Method }}" {{ form_attr .Form }}>
|
||||
// {{- form_error .Form nil -}}
|
||||
//
|
||||
// {{- form_help .Form -}}
|
||||
//
|
||||
// {{- range $field := .Form.Fields -}}
|
||||
// {{- form_row $field -}}
|
||||
// {{- end -}}
|
||||
// </form>`,
|
||||
// "attributes": `{{ range $key, $value := .Attributes }}{{ $key }}="{{ $value }}"{{ end }}`,
|
||||
// "help": `
|
||||
// {{- if gt (len .Help) 0 -}}
|
||||
// <div class="form-help">{{ .Help }}</div>
|
||||
// {{- end -}}
|
||||
// `,
|
||||
// "label": `
|
||||
// {{ if .Field.HasOption "label" }}
|
||||
// {{ $label := (.Field.GetOption "label").Value }}
|
||||
//
|
||||
// {{- if ne $label "" -}}
|
||||
// <label for="{{ .Field.GetId }}" {{ form_label_attr .Field }} class="form-label">{{ $label }}</label>
|
||||
// {{- end -}}
|
||||
// {{- end -}}
|
||||
// `,
|
||||
// "input": `
|
||||
// {{- $type := .Field.GetOption "type" -}}
|
||||
// {{- $checked := and (eq (.Field.GetOption "type").Value "checkbox") (.Field.Data) -}}
|
||||
// {{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
// {{- $value := .Field.Data -}}
|
||||
// {{- $class := "form-control" }}
|
||||
//
|
||||
// {{- if eq $type.Value "checkbox" -}}
|
||||
// {{- $value = 1 -}}
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{- if or (eq $type.Value "checkbox") (eq $type.Value "radio") -}}
|
||||
// {{- $class = "form-check-input" -}}
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{- if eq $type.Value "range" -}}
|
||||
// {{- $class = "form-range" -}}
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{- if or (eq $type.Value "submit") (eq $type.Value "reset") (eq $type.Value "button") -}}
|
||||
// {{- $class = "" -}}
|
||||
//
|
||||
// {{ if .Field.HasOption "attr" }}
|
||||
// {{ $class = (.Field.GetOption "attr").Value.attr.class }}
|
||||
// {{ end }}
|
||||
// {{- end -}}
|
||||
//
|
||||
// <input id="{{ .Field.GetId }}" {{ if $checked }}checked{{ end }} {{ if $required }}required="required"{{ end }} name="{{ .Field.GetName }}" value="{{ $value }}" type="{{ $type.Value }}" {{ form_widget_attr .Field }} class="{{ $class }}">
|
||||
// `,
|
||||
// "textarea": `
|
||||
// <textarea id="{{ .Field.GetId }}" {{ if .Field.HasOption "required" }}{{ if (.Field.GetOption "required").Value }}required="required"{{ end }}{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }} class="form-control">{{ .Field.Data }}</textarea>
|
||||
// `,
|
||||
// "choice": `
|
||||
// {{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
// {{- $isExpanded := (.Field.GetOption "expanded").Value -}}
|
||||
// {{- $isMultiple := (.Field.GetOption "multiple").Value -}}
|
||||
// {{- $emptyChoiceLabel := (.Field.GetOption "empty_choice_label").Value -}}
|
||||
// {{- $choices := (.Field.GetOption "choices").Value -}}
|
||||
// {{- $field := .Field -}}
|
||||
// {{- $keyAdd := 0 -}}
|
||||
//
|
||||
// {{- if and (not $required) (not $isMultiple) -}}
|
||||
// {{- $keyAdd = 1 -}}
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{- if $isExpanded -}}
|
||||
// {{- if and (not $required) (not $isMultiple) -}}
|
||||
// <div class="form-check">
|
||||
// <input value="" {{ if not $field.Data }}checked{{ end }} name="{{ $field.GetName }}" type="radio" id="{{ $field.GetId }}-0" class="form-check-input">
|
||||
// <label for="{{ $field.GetId }}-0" class="form-check-label">{{ ($field.GetOption "empty_choice_label").Value }}</label>
|
||||
// </div>
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{- range $key, $choice := $choices.GetChoices -}}
|
||||
// <div class="form-check">
|
||||
// <input name="{{ $field.GetName }}" type="{{ if $isMultiple }}checkbox{{ else }}radio{{ end }}" value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}checked{{ end }} id="{{ $field.GetId }}-{{ sum $key $keyAdd }}" class="form-check-input">
|
||||
// <label for="{{ $field.GetId }}-{{ sum $key $keyAdd }}" class="form-check-label">{{- $choice.Label -}}</label>
|
||||
// </div>
|
||||
// {{- end -}}
|
||||
// {{- else -}}
|
||||
// <select id="{{ .Field.GetId }}" {{ if $required }}required="required"{{ end }} {{ if $isMultiple }}multiple{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }} class="form-select">
|
||||
// {{- if and (not $required) (not $isMultiple) -}}
|
||||
// <option value="">{{ $emptyChoiceLabel }}</option>
|
||||
// {{- end -}}
|
||||
// {{- range $choice := $choices.GetChoices -}}
|
||||
// <option value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}selected{{ end }}>{{ $choice.Label }}</option>
|
||||
// {{- end -}}
|
||||
// </select>
|
||||
// {{- end -}}
|
||||
// `,
|
||||
// "sub_form": `
|
||||
// <fieldset id="{{ .Field.GetId }}">
|
||||
// {{ if .Field.HasOption "label" }}
|
||||
// {{ $label := (.Field.GetOption "label").Value }}
|
||||
//
|
||||
// {{- if ne $label "" -}}
|
||||
// <legend {{ form_label_attr .Field }}>{{ $label }}</legend>
|
||||
// {{- end -}}
|
||||
// {{- end -}}
|
||||
//
|
||||
// {{ form_widget_help .Field }}
|
||||
//
|
||||
// {{- range $field := .Field.Children -}}
|
||||
// {{- form_row $field -}}
|
||||
// {{- end -}}
|
||||
// </fieldset>
|
||||
// `,
|
||||
// "error": `
|
||||
// {{- if gt (len .Errors) 0 -}}
|
||||
// <div class="invalid-feedback d-block">
|
||||
// {{- range $error := .Errors -}}
|
||||
// <div>{{- $error -}}</div>
|
||||
// {{- end -}}
|
||||
// </div>
|
||||
// {{- end -}}
|
||||
// `,
|
||||
// "row": `<div {{ form_row_attr .Field }}>
|
||||
// {{ $labelAfterWidget := and (.Field.HasOption "type") (eq (.Field.GetOption "type").Value "checkbox") }}
|
||||
//
|
||||
// {{ if and (eq (len .Field.Children) 0) (not $labelAfterWidget) }}
|
||||
// {{- form_label .Field -}}
|
||||
// {{ end }}
|
||||
//
|
||||
// {{- form_widget .Field -}}
|
||||
// {{- form_error nil .Field -}}
|
||||
//
|
||||
// {{ if and (eq (len .Field.Children) 0) ($labelAfterWidget) }}
|
||||
// {{- form_label .Field -}}
|
||||
// {{ end }}
|
||||
//
|
||||
// {{- form_widget_help .Field -}}
|
||||
// </div>`,
|
||||
// }
|
||||
|
|
|
|||
464
theme/html5.go
464
theme/html5.go
|
|
@ -1,5 +1,15 @@
|
|||
package theme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"gitnet.fr/deblan/go-form/form"
|
||||
"gitnet.fr/deblan/go-form/validation"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
// @license GNU AGPL version 3 or any later version
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -15,120 +25,370 @@ package theme
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var Html5 = map[string]string{
|
||||
"form": `<form action="{{ .Form.Action }}" method="{{ .Form.Method }}" {{ form_attr .Form }}>
|
||||
{{- form_error .Form nil -}}
|
||||
var Html5 = CreateTheme(func() map[string]RenderFunc {
|
||||
theme := make(map[string]RenderFunc)
|
||||
|
||||
{{- form_help .Form -}}
|
||||
theme["attributes"] = func(args ...any) Node {
|
||||
var result []Node
|
||||
|
||||
{{- range $field := .Form.Fields -}}
|
||||
{{- form_row $field -}}
|
||||
{{- end -}}
|
||||
</form>`,
|
||||
"attributes": `{{ range $key, $value := .Attributes }}{{ $key }}="{{ $value }}"{{ end }}`,
|
||||
"help": `
|
||||
{{- if gt (len .Help) 0 -}}
|
||||
<div class="form-help">{{ .Help }}</div>
|
||||
{{- end -}}
|
||||
`,
|
||||
"label": `
|
||||
{{ if .Field.HasOption "label" }}
|
||||
{{ $label := (.Field.GetOption "label").Value }}
|
||||
for i, v := range args[0].(map[string]string) {
|
||||
result = append(result, Attr(i, v))
|
||||
}
|
||||
|
||||
{{- if ne $label "" -}}
|
||||
<label for="{{ .Field.GetId }}" {{ form_label_attr .Field }}>{{ $label }}</label>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
`,
|
||||
"input": `
|
||||
{{- $type := .Field.GetOption "type" -}}
|
||||
{{- $checked := and (eq (.Field.GetOption "type").Value "checkbox") (.Field.Data) -}}
|
||||
{{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
{{- $value := .Field.Data -}}
|
||||
return Group(result)
|
||||
}
|
||||
|
||||
{{- if eq $type.Value "checkbox" -}}
|
||||
{{- $value = 1 -}}
|
||||
{{- end -}}
|
||||
theme["form_attributes"] = func(args ...any) Node {
|
||||
form := args[0].(*form.Form)
|
||||
|
||||
<input id="{{ .Field.GetId }}" {{ if $checked }}checked{{ end }} {{ if $required }}required="required"{{ end }} name="{{ .Field.GetName }}" value="{{ $value }}" type="{{ $type.Value }}" {{ form_widget_attr .Field }}>
|
||||
`,
|
||||
"textarea": `
|
||||
<textarea id="{{ .Field.GetId }}" {{ if .Field.HasOption "required" }}{{ if (.Field.GetOption "required").Value }}required="required"{{ end }}{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }}>{{ .Field.Data }}</textarea>
|
||||
`,
|
||||
"choice": `
|
||||
{{- $required := and (.Field.HasOption "required") (.Field.GetOption "required").Value -}}
|
||||
{{- $isExpanded := (.Field.GetOption "expanded").Value -}}
|
||||
{{- $isMultiple := (.Field.GetOption "multiple").Value -}}
|
||||
{{- $emptyChoiceLabel := (.Field.GetOption "empty_choice_label").Value -}}
|
||||
{{- $choices := (.Field.GetOption "choices").Value -}}
|
||||
{{- $field := .Field -}}
|
||||
{{- $keyAdd := 0 -}}
|
||||
if !form.HasOption("attr") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
{{- $keyAdd = 1 -}}
|
||||
{{- end -}}
|
||||
return theme["attributes"](form.GetOption("attr").AsMapString())
|
||||
}
|
||||
|
||||
{{- if $isExpanded -}}
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
<input value="" {{ if not $field.Data }}checked{{ end }} name="{{ $field.GetName }}" type="radio" id="{{ $field.GetId }}-0">
|
||||
<label for="{{ $field.GetId }}-0">{{ ($field.GetOption "empty_choice_label").Value }}</label>
|
||||
{{- end -}}
|
||||
theme["errors"] = func(args ...any) Node {
|
||||
errors := args[0].([]validation.Error)
|
||||
|
||||
{{- range $key, $choice := $choices.GetChoices -}}
|
||||
<input name="{{ $field.GetName }}" type="{{ if $isMultiple }}checkbox{{ else }}radio{{ end }}" value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}checked{{ end }} id="{{ $field.GetId }}-{{ sum $key $keyAdd }}">
|
||||
<label for="{{ $field.GetId }}-{{ sum $key $keyAdd }}">{{- $choice.Label -}}</label>
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
<select id="{{ .Field.GetId }}" {{ if $required }}required="required"{{ end }} {{ if $isMultiple }}multiple{{ end }} name="{{ .Field.GetName }}" {{ form_widget_attr .Field }}>
|
||||
{{- if and (not $required) (not $isMultiple) -}}
|
||||
<option value="">{{ $emptyChoiceLabel }}</option>
|
||||
{{- end -}}
|
||||
{{- range $choice := $choices.GetChoices -}}
|
||||
<option value="{{ $choice.Value }}" {{ if $choices.Match $field $choice.Value }}selected{{ end }}>{{ $choice.Label }}</option>
|
||||
{{- end -}}
|
||||
</select>
|
||||
{{- end -}}
|
||||
`,
|
||||
"sub_form": `
|
||||
<fieldset id="{{ .Field.GetId }}">
|
||||
{{ if .Field.HasOption "label" }}
|
||||
{{ $label := (.Field.GetOption "label").Value }}
|
||||
var result []Node
|
||||
|
||||
{{- if ne $label "" -}}
|
||||
<legend {{ form_label_attr .Field }}>{{ $label }}</legend>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
for _, v := range errors {
|
||||
result = append(result, Li(Text(string(v))))
|
||||
}
|
||||
|
||||
{{ form_widget_help .Field }}
|
||||
return Ul(
|
||||
Class("form-errors"),
|
||||
Group(result),
|
||||
)
|
||||
}
|
||||
|
||||
{{- range $field := .Field.Children -}}
|
||||
{{- form_row $field -}}
|
||||
{{- end -}}
|
||||
</fieldset>
|
||||
`,
|
||||
"error": `
|
||||
{{- if gt (len .Errors) 0 -}}
|
||||
<ul class="form-errors">
|
||||
{{- range $error := .Errors -}}
|
||||
<li class="form-error">{{- $error -}}</li>
|
||||
{{- end -}}
|
||||
</ul>
|
||||
{{- end -}}
|
||||
`,
|
||||
"row": `<div {{ form_row_attr .Field }}>
|
||||
{{ $labelAfterWidget := and (.Field.HasOption "type") (eq (.Field.GetOption "type").Value "checkbox") }}
|
||||
theme["form_errors"] = func(args ...any) Node {
|
||||
form := args[0].(*form.Form)
|
||||
|
||||
{{ if and (eq (len .Field.Children) 0) (not $labelAfterWidget) }}
|
||||
{{- form_label .Field -}}
|
||||
{{ end }}
|
||||
return If(
|
||||
len(form.Errors) > 0,
|
||||
theme["errors"](form.Errors),
|
||||
)
|
||||
}
|
||||
|
||||
{{- form_error nil .Field -}}
|
||||
{{- form_widget .Field -}}
|
||||
theme["form_widget_errors"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
{{ if and (eq (len .Field.Children) 0) ($labelAfterWidget) }}
|
||||
{{- form_label .Field -}}
|
||||
{{ end }}
|
||||
return If(
|
||||
len(field.Errors) > 0,
|
||||
theme["errors"](field.Errors),
|
||||
)
|
||||
}
|
||||
|
||||
{{- form_widget_help .Field -}}
|
||||
</div>`,
|
||||
}
|
||||
theme["help"] = func(args ...any) Node {
|
||||
help := args[0].(string)
|
||||
|
||||
if len(help) == 0 {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
return Div(
|
||||
Class("form-help"),
|
||||
Text("ok"),
|
||||
)
|
||||
}
|
||||
|
||||
theme["form_help"] = func(args ...any) Node {
|
||||
form := args[0].(*form.Form)
|
||||
|
||||
if !form.HasOption("help") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
return theme["help"](form.GetOption("help").AsString())
|
||||
}
|
||||
|
||||
theme["form_widget_help"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
if !field.HasOption("help") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
return theme["help"](field.GetOption("help").AsString())
|
||||
}
|
||||
|
||||
theme["label_attributes"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
if !field.HasOption("label_attr") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
return theme["attributes"](field.GetOption("label_attr").AsMapString())
|
||||
}
|
||||
|
||||
theme["form_label"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
if !field.HasOption("label") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
label := field.GetOption("label").AsString()
|
||||
|
||||
return If(len(label) > 0, Label(
|
||||
Class("form-label"),
|
||||
For(field.GetId()),
|
||||
theme["label_attributes"](field),
|
||||
Text(label),
|
||||
))
|
||||
}
|
||||
|
||||
theme["field_attributes"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
if !field.HasOption("attr") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
return theme["attributes"](field.GetOption("attr").AsMapString())
|
||||
}
|
||||
|
||||
theme["textarea_attributes"] = func(args ...any) Node {
|
||||
return theme["field_attributes"](args...)
|
||||
}
|
||||
|
||||
theme["input_attributes"] = func(args ...any) Node {
|
||||
return theme["field_attributes"](args...)
|
||||
}
|
||||
|
||||
theme["sub_form_attributes"] = func(args ...any) Node {
|
||||
return theme["field_attributes"](args...)
|
||||
}
|
||||
|
||||
theme["input"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
fieldType := "text"
|
||||
|
||||
if field.HasOption("type") {
|
||||
fieldType = field.GetOption("type").AsString()
|
||||
}
|
||||
|
||||
value := cast.ToString(field.Data)
|
||||
|
||||
if fieldType == "checkbox" {
|
||||
value = "1"
|
||||
}
|
||||
|
||||
return Input(
|
||||
Name(field.GetName()),
|
||||
ID(field.GetId()),
|
||||
Type(fieldType),
|
||||
Value(value),
|
||||
If(fieldType == "checkbox" && field.Data != false, Checked()),
|
||||
If(field.HasOption("required") && field.GetOption("required").AsBool(), Required()),
|
||||
theme["input_attributes"](field),
|
||||
)
|
||||
}
|
||||
|
||||
theme["choice_options"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
choices := field.GetOption("choices").Value.(*form.Choices)
|
||||
|
||||
isRequired := field.HasOption("required") && field.GetOption("required").AsBool()
|
||||
isMultiple := field.GetOption("multiple").AsBool()
|
||||
|
||||
var options []Node
|
||||
|
||||
if !isMultiple && !isRequired {
|
||||
options = append(options, Option(
|
||||
Text(field.GetOption("empty_choice_label").AsString()),
|
||||
))
|
||||
}
|
||||
|
||||
for _, choice := range choices.GetChoices() {
|
||||
options = append(options, Option(
|
||||
Value(choice.Value),
|
||||
Text(choice.Label),
|
||||
If(choices.Match(field, choice.Value), Selected()),
|
||||
))
|
||||
}
|
||||
|
||||
return Group(options)
|
||||
}
|
||||
|
||||
theme["choice_expanded"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
choices := field.GetOption("choices").Value.(*form.Choices)
|
||||
|
||||
isRequired := field.HasOption("required") && field.GetOption("required").AsBool()
|
||||
isMultiple := field.GetOption("multiple").AsBool()
|
||||
noneLabel := field.GetOption("empty_choice_label").AsString()
|
||||
|
||||
var items []Node
|
||||
|
||||
if !isMultiple && !isRequired {
|
||||
id := fmt.Sprintf("%s-%s", field.GetId(), "none")
|
||||
|
||||
items = append(items, Group([]Node{
|
||||
Input(
|
||||
Name(field.GetName()),
|
||||
ID(id),
|
||||
Value(""),
|
||||
Type("radio"),
|
||||
theme["input_attributes"](field),
|
||||
If(cast.ToString(field.Data) == "", Checked()),
|
||||
),
|
||||
Label(For(id), Text(noneLabel)),
|
||||
}))
|
||||
}
|
||||
|
||||
for key, choice := range choices.GetChoices() {
|
||||
id := fmt.Sprintf("%s-%d", field.GetId(), key)
|
||||
|
||||
items = append(items, Group([]Node{
|
||||
Input(
|
||||
Name(field.GetName()),
|
||||
ID(id),
|
||||
Value(choice.Value),
|
||||
If(isMultiple, Type("checkbox")),
|
||||
If(!isMultiple, Type("radio")),
|
||||
theme["input_attributes"](field),
|
||||
If(choices.Match(field, choice.Value), Checked()),
|
||||
),
|
||||
Label(For(id), Text(choice.Label)),
|
||||
}))
|
||||
}
|
||||
|
||||
return Group(items)
|
||||
}
|
||||
|
||||
theme["choice"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
isRequired := field.HasOption("required") && field.GetOption("required").AsBool()
|
||||
isExpanded := field.GetOption("expanded").AsBool()
|
||||
isMultiple := field.GetOption("multiple").AsBool()
|
||||
noneLabel := field.GetOption("empty_choice_label").AsString()
|
||||
|
||||
_ = noneLabel
|
||||
|
||||
if isExpanded {
|
||||
return theme["choice_expanded"](field)
|
||||
} else {
|
||||
return Select(
|
||||
ID(field.GetId()),
|
||||
If(isRequired, Required()),
|
||||
If(isMultiple, Multiple()),
|
||||
Name(field.GetName()),
|
||||
theme["choice_options"](field),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
theme["textarea"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
return Textarea(
|
||||
Name(field.GetName()),
|
||||
ID(field.GetId()),
|
||||
If(field.HasOption("required") && field.GetOption("required").AsBool(), Required()),
|
||||
theme["textarea_attributes"](field),
|
||||
Text(cast.ToString(field.Data)),
|
||||
)
|
||||
}
|
||||
|
||||
theme["sub_form_label"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
if !field.HasOption("label") {
|
||||
return Raw("")
|
||||
}
|
||||
|
||||
label := field.GetOption("label").AsString()
|
||||
|
||||
return If(len(label) > 0, Legend(
|
||||
Class("form-label"),
|
||||
theme["label_attributes"](field),
|
||||
Text(label),
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
theme["sub_form_content"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
return theme["form_fields"](field.Children)
|
||||
}
|
||||
|
||||
theme["sub_form"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
return FieldSet(
|
||||
ID(field.GetId()),
|
||||
theme["sub_form_label"](field),
|
||||
theme["sub_form_attributes"](field),
|
||||
theme["sub_form_content"](field),
|
||||
)
|
||||
}
|
||||
|
||||
theme["form_widget"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
tpl, ok := theme[field.Widget]
|
||||
|
||||
if !ok {
|
||||
return Raw("Invalid field widget: " + field.Widget)
|
||||
}
|
||||
|
||||
return tpl(field)
|
||||
}
|
||||
|
||||
theme["form_row"] = func(args ...any) Node {
|
||||
field := args[0].(*form.Field)
|
||||
|
||||
isCheckbox := field.HasOption("type") && field.GetOption("type").AsString() == "checkbox"
|
||||
hasChildren := len(field.Children) > 0
|
||||
labelAfter := isCheckbox && !hasChildren
|
||||
label := theme["form_label"](field)
|
||||
|
||||
return Div(
|
||||
Class("form-row"),
|
||||
If(!labelAfter, label),
|
||||
theme["form_widget_errors"](field),
|
||||
theme["form_widget"](field),
|
||||
If(labelAfter, label),
|
||||
theme["form_widget_help"](field),
|
||||
)
|
||||
}
|
||||
|
||||
theme["form_fields"] = func(args ...any) Node {
|
||||
var items []Node
|
||||
|
||||
for _, item := range args[0].([]*form.Field) {
|
||||
items = append(items, theme["form_row"](item))
|
||||
}
|
||||
|
||||
return Group(items)
|
||||
}
|
||||
|
||||
theme["form_content"] = func(args ...any) Node {
|
||||
form := args[0].(*form.Form)
|
||||
|
||||
return Div(
|
||||
theme["form_errors"](form),
|
||||
theme["form_help"](form),
|
||||
theme["form_fields"](form.Fields),
|
||||
)
|
||||
}
|
||||
|
||||
theme["form"] = func(args ...any) Node {
|
||||
form := args[0].(*form.Form)
|
||||
|
||||
return Form(
|
||||
Action(form.Action),
|
||||
Method(form.Method),
|
||||
theme["form_attributes"](form),
|
||||
theme["form_content"](form),
|
||||
)
|
||||
}
|
||||
|
||||
return theme
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,172 +19,38 @@ import (
|
|||
"bytes"
|
||||
"html/template"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"gitnet.fr/deblan/go-form/form"
|
||||
"gitnet.fr/deblan/go-form/validation"
|
||||
"maragu.dev/gomponents"
|
||||
)
|
||||
|
||||
type RenderFunc func(args ...any) gomponents.Node
|
||||
|
||||
type Renderer struct {
|
||||
Theme map[string]string
|
||||
Theme map[string]RenderFunc
|
||||
}
|
||||
|
||||
func NewRenderer(theme map[string]string) *Renderer {
|
||||
func NewRenderer(theme map[string]RenderFunc) *Renderer {
|
||||
r := new(Renderer)
|
||||
r.Theme = theme
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderForm(form *form.Form) template.HTML {
|
||||
return r.Render("form", r.Theme["form"], map[string]any{
|
||||
"Form": form,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderRow(field *form.Field) template.HTML {
|
||||
return r.Render("row", r.Theme["row"], map[string]any{
|
||||
"Field": field,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderLabel(field *form.Field) template.HTML {
|
||||
return r.Render("label", r.Theme["label"], map[string]any{
|
||||
"Field": field,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderWidget(field *form.Field) template.HTML {
|
||||
return r.Render("widget", r.Theme[field.Widget], map[string]any{
|
||||
"Field": field,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderError(form *form.Form, field *form.Field) template.HTML {
|
||||
var errors []validation.Error
|
||||
|
||||
if field != nil {
|
||||
errors = field.Errors
|
||||
}
|
||||
|
||||
if form != nil {
|
||||
errors = form.Errors
|
||||
}
|
||||
|
||||
return r.Render("error", r.Theme["error"], map[string]any{
|
||||
"Errors": errors,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderLabelAttr(field *form.Field) template.HTMLAttr {
|
||||
var attributes map[string]string
|
||||
|
||||
if field.HasOption("label_attr") {
|
||||
attributes = field.GetOption("label_attr").Value.(map[string]string)
|
||||
}
|
||||
|
||||
return r.RenderAttr("label_attr", r.Theme["attributes"], map[string]any{
|
||||
"Attributes": attributes,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderWidgetAttr(field *form.Field) template.HTMLAttr {
|
||||
var attributes map[string]string
|
||||
|
||||
if field.HasOption("attr") {
|
||||
attributes = field.GetOption("attr").Value.(map[string]string)
|
||||
}
|
||||
|
||||
return r.RenderAttr("widget_attr", r.Theme["attributes"], map[string]any{
|
||||
"Attributes": attributes,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderFormAttr(form *form.Form) template.HTMLAttr {
|
||||
var attributes map[string]string
|
||||
|
||||
if form.HasOption("attr") {
|
||||
attributes = form.GetOption("attr").Value.(map[string]string)
|
||||
}
|
||||
|
||||
return r.RenderAttr("form_attr", r.Theme["attributes"], map[string]any{
|
||||
"Attributes": attributes,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderRowAttr(field *form.Field) template.HTMLAttr {
|
||||
var attributes map[string]string
|
||||
|
||||
if field.HasOption("row_attr") {
|
||||
attributes = field.GetOption("row_attr").Value.(map[string]string)
|
||||
}
|
||||
|
||||
return r.RenderAttr("raw_attr", r.Theme["attributes"], map[string]any{
|
||||
"Attributes": attributes,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderFormHelp(form *form.Form) template.HTML {
|
||||
var help string
|
||||
|
||||
if form.HasOption("help") {
|
||||
help = form.GetOption("help").Value.(string)
|
||||
}
|
||||
|
||||
return r.Render("help", r.Theme["help"], map[string]any{
|
||||
"Help": help,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderWidgetHelp(field *form.Field) template.HTML {
|
||||
var help string
|
||||
|
||||
if field.HasOption("help") {
|
||||
help = field.GetOption("help").Value.(string)
|
||||
}
|
||||
|
||||
return r.Render("help", r.Theme["help"], map[string]any{
|
||||
"Help": help,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderAttr(name, tpl string, args any) template.HTMLAttr {
|
||||
t, err := template.New(name).Parse(tpl)
|
||||
|
||||
if err != nil {
|
||||
return template.HTMLAttr("")
|
||||
}
|
||||
|
||||
func toTemplateHtml(n gomponents.Node) template.HTML {
|
||||
var buf bytes.Buffer
|
||||
err = t.Execute(&buf, args)
|
||||
|
||||
if err != nil {
|
||||
return template.HTMLAttr("")
|
||||
}
|
||||
n.Render(&buf)
|
||||
|
||||
return template.HTMLAttr(buf.String())
|
||||
return template.HTML(buf.String())
|
||||
}
|
||||
|
||||
func (r *Renderer) RenderForm(form *form.Form) template.HTML {
|
||||
return toTemplateHtml(r.Theme["form"](form))
|
||||
}
|
||||
|
||||
func (r *Renderer) FuncMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"form": r.RenderForm,
|
||||
"form_row": r.RenderRow,
|
||||
"form_label": r.RenderLabel,
|
||||
"form_widget": r.RenderWidget,
|
||||
"form_error": r.RenderError,
|
||||
"form_attr": r.RenderFormAttr,
|
||||
"form_widget_attr": r.RenderWidgetAttr,
|
||||
"form_label_attr": r.RenderLabelAttr,
|
||||
"form_row_attr": r.RenderRowAttr,
|
||||
"form_help": r.RenderFormHelp,
|
||||
"form_widget_help": r.RenderWidgetHelp,
|
||||
"sum": func(values ...any) float64 {
|
||||
res := float64(0)
|
||||
for _, value := range values {
|
||||
res += cast.ToFloat64(value)
|
||||
}
|
||||
|
||||
return res
|
||||
},
|
||||
"form": r.RenderForm,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
16
theme/theme.go
Normal file
16
theme/theme.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package theme
|
||||
|
||||
func CreateTheme(generator func() map[string]RenderFunc) map[string]RenderFunc {
|
||||
return generator()
|
||||
}
|
||||
|
||||
func ExtendTheme(base map[string]RenderFunc, generator func() map[string]RenderFunc) map[string]RenderFunc {
|
||||
extended := CreateTheme(generator)
|
||||
|
||||
for i, v := range base {
|
||||
extended[i] = v
|
||||
extended["base_"+i] = v
|
||||
}
|
||||
|
||||
return extended
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue