-
-
- `)
-
- var dump godump.Dumper
- dump.Theme = godump.Theme{}
-
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- tpl.Execute(w, map[string]any{
- "Form": f,
- "Dump": template.HTML(dump.Sprint(data)),
- })
- })
-
- log.Fatal(http.ListenAndServe(":1122", nil))
-}
diff --git a/theme/html5.go b/theme/html5.go
index a441e48..2d7889e 100644
--- a/theme/html5.go
+++ b/theme/html5.go
@@ -1,6 +1,7 @@
package theme
import (
+ "bytes"
"fmt"
"github.com/spf13/cast"
@@ -58,6 +59,7 @@ var Html5 = CreateTheme(func() map[string]RenderFunc {
}
return Ul(
+ Class("gf-errors"),
Group(result),
)
}
@@ -93,6 +95,7 @@ var Html5 = CreateTheme(func() map[string]RenderFunc {
}
return Div(
+ Class("gf-help"),
Text(help),
extra,
)
@@ -348,6 +351,50 @@ var Html5 = CreateTheme(func() map[string]RenderFunc {
)
}
+ theme["collection"] = func(parent map[string]RenderFunc, args ...any) Node {
+ field := args[0].(*form.Field)
+
+ var prototype string
+
+ if opt := field.GetOption("form"); opt != nil {
+ if val, ok := opt.Value.(*form.Form); ok {
+ var buffer bytes.Buffer
+ dest := form.NewFieldSubForm(field.Name)
+
+ for _, c := range val.Fields {
+ child := c.Copy()
+ child.NamePrefix = "[__name__]"
+ dest.Add(child)
+ }
+
+ fieldPrototype := parent["form_row"](parent, dest)
+ fieldPrototype.Render(&buffer)
+
+ prototype = buffer.String()
+ }
+ }
+
+ field.WithOptions(form.NewOption("prototype", prototype))
+ field.Widget = "collection_build"
+
+ return parent["form_widget"](parent, field)
+ }
+
+ theme["collection_build"] = func(parent map[string]RenderFunc, args ...any) Node {
+ field := args[0].(*form.Field)
+ prototype := field.GetOption("prototype").AsString()
+ var items []Node
+
+ for _, child := range field.Children {
+ items = append(items, parent["form_row"](parent, child))
+ }
+
+ return Div(
+ Attr("data-prototype", prototype),
+ Group(items),
+ )
+ }
+
theme["form_widget"] = func(parent map[string]RenderFunc, args ...any) Node {
field := args[0].(*form.Field)
@@ -407,6 +454,7 @@ var Html5 = CreateTheme(func() map[string]RenderFunc {
form := args[0].(*form.Form)
return Form(
+ Class("gf-form"),
Action(form.Action),
Method(form.Method),
parent["form_attributes"](parent, form),
diff --git a/util/collection.go b/util/collection.go
new file mode 100644
index 0000000..f0aa649
--- /dev/null
+++ b/util/collection.go
@@ -0,0 +1,111 @@
+package util
+
+// @license GNU AGPL version 3 or any later version
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+import (
+ "regexp"
+ "strings"
+
+ "github.com/spf13/cast"
+)
+
+type CollectionValue struct {
+ Name string
+ Value string
+ Children map[string]*CollectionValue
+}
+
+type Collection struct {
+ Children map[int]*CollectionValue
+}
+
+func NewCollection() *Collection {
+ return &Collection{
+ Children: make(map[int]*CollectionValue),
+ }
+}
+
+func NewCollectionValue(name string) *CollectionValue {
+ return &CollectionValue{
+ Name: name,
+ Children: make(map[string]*CollectionValue),
+ }
+}
+
+func (c *Collection) Add(indexes []string, value string) {
+ firstIndex := cast.ToInt(indexes[0])
+ size := len(indexes)
+ child := c.Children[firstIndex]
+
+ if child == nil {
+ child = NewCollectionValue(indexes[0])
+ c.Children[firstIndex] = child
+ }
+
+ child.Add(indexes[1:size], value, nil)
+}
+
+func (c *Collection) Slice() []any {
+ var result []any
+
+ for _, child := range c.Children {
+ result = append(result, child.Map())
+ }
+
+ return result
+}
+
+func (c *CollectionValue) Map() any {
+ if len(c.Children) == 0 {
+ return c.Value
+ }
+
+ results := make(map[string]any)
+
+ for _, child := range c.Children {
+ results[child.Name] = child.Map()
+ }
+
+ return results
+}
+
+func (c *CollectionValue) Add(indexes []string, value string, lastChild *CollectionValue) {
+ size := len(indexes)
+
+ if size > 0 {
+ firstIndex := indexes[0]
+ child := c.Children[firstIndex]
+
+ child = NewCollectionValue(indexes[0])
+ c.Children[firstIndex] = child
+
+ child.Add(indexes[1:size], value, child)
+ } else {
+ lastChild.Value = value
+ }
+}
+
+func ExtractDataIndexes(value string) []string {
+ re := regexp.MustCompile(`\[[^\]]+\]`)
+ items := re.FindAll([]byte(value), -1)
+ var results []string
+
+ for _, i := range items {
+ results = append(results, strings.Trim(string(i), "[]"))
+ }
+
+ return results
+}
diff --git a/util/inspect.go b/util/inspect.go
index 17c0839..22fa4f4 100644
--- a/util/inspect.go
+++ b/util/inspect.go
@@ -29,6 +29,10 @@ func InspectStruct(input interface{}) (map[string]interface{}, error) {
val = val.Elem()
}
+ if val.Kind() == reflect.Map {
+ return input.(map[string]interface{}), nil
+ }
+
if val.Kind() != reflect.Struct {
return nil, errors.New("Invalid type")
}
diff --git a/util/transformer.go b/util/transformer.go
index 743b117..a942fa1 100644
--- a/util/transformer.go
+++ b/util/transformer.go
@@ -1,5 +1,20 @@
package util
+// @license GNU AGPL version 3 or any later version
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
import (
"fmt"
"net/url"