personal-site/pkg/ui/components/form.go

268 lines
4.6 KiB
Go

package components
import (
"github.com/camzawacki/personal-site/pkg/form"
"github.com/camzawacki/personal-site/pkg/ui"
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
)
type (
InputFieldParams struct {
Form form.Form
FormField string
Name string
InputType string
Label string
Value string
Placeholder string
Help string
}
FileFieldParams struct {
Name string
Label string
Help string
}
OptionsParams struct {
Form form.Form
FormField string
Name string
Label string
Value string
Options []Choice
Help string
}
Choice struct {
Value string
Label string
}
TextareaFieldParams struct {
Form form.Form
FormField string
Name string
Label string
Value string
Help string
}
CheckboxParams struct {
Form form.Form
FormField string
Name string
Label string
Checked bool
}
)
func ControlGroup(controls ...Node) Node {
return Div(
Class("mt-2 flex gap-2"),
Group(controls),
)
}
func TextareaField(el TextareaFieldParams) Node {
return Fieldset(
el.Label,
Textarea(
Class("textarea h-24 w-2/3 "+formFieldStatusClass(el.Form, el.FormField)),
ID(el.Name),
Name(el.Name),
Text(el.Value),
),
Help(el.Help),
formFieldErrors(el.Form, el.FormField),
)
}
func Radios(el OptionsParams) Node {
buttons := make(Group, len(el.Options))
for i, opt := range el.Options {
id := "radio-" + el.Name + "-" + opt.Value
buttons[i] = Div(
Class("mb-2"),
Input(
ID(id),
Type("radio"),
Name(el.Name),
Value(opt.Value),
Class("radio mr-1 "+formFieldStatusClass(el.Form, el.FormField)),
If(el.Value == opt.Value, Checked()),
),
Label(
Text(opt.Label),
For(id),
),
)
}
return Fieldset(
el.Label,
buttons,
formFieldErrors(el.Form, el.FormField),
)
}
func SelectList(el OptionsParams) Node {
buttons := make(Group, len(el.Options))
for i, opt := range el.Options {
buttons[i] = Option(
Text(opt.Label),
Value(opt.Value),
If(opt.Value == el.Value, Attr("selected")),
)
}
return Fieldset(
el.Label,
Select(
Class("select "+formFieldStatusClass(el.Form, el.FormField)),
Name(el.Name),
buttons,
),
Help(el.Help),
formFieldErrors(el.Form, el.FormField),
)
}
func Checkbox(el CheckboxParams) Node {
return Div(
Label(
Class("label"),
Input(
Class("checkbox"),
Type("checkbox"),
Name(el.Name),
If(el.Checked, Checked()),
Value("true"),
),
Text(" "+el.Label),
),
formFieldErrors(el.Form, el.FormField),
)
}
func InputField(el InputFieldParams) Node {
return Fieldset(
el.Label,
Input(
ID(el.Name),
Name(el.Name),
Type(el.InputType),
Class("input "+formFieldStatusClass(el.Form, el.FormField)),
Value(el.Value),
If(el.Placeholder != "", Placeholder(el.Placeholder)),
),
Help(el.Help),
formFieldErrors(el.Form, el.FormField),
)
}
func Help(text string) Node {
return If(len(text) > 0, Div(
Class("label"),
Text(text),
))
}
func Fieldset(label string, els ...Node) Node {
return FieldSet(
Class("fieldset"),
If(len(label) > 0, Legend(
Class("fieldset-legend"),
Text(label),
)),
Group(els),
)
}
func FileField(el FileFieldParams) Node {
return Fieldset(
el.Label,
Input(
Type("file"),
Class("file-input"),
Name(el.Name),
),
Help(el.Help),
)
}
func formFieldStatusClass(fm form.Form, formField string) string {
switch {
case fm == nil:
return ""
case !fm.IsSubmitted():
return ""
case fm.FieldHasErrors(formField):
return "input-error"
default:
return "input-success"
}
}
func formFieldErrors(fm form.Form, field string) Node {
if fm == nil {
return nil
}
errs := fm.GetFieldErrors(field)
if len(errs) == 0 {
return nil
}
g := make(Group, len(errs))
for i, err := range errs {
g[i] = Div(
Class("text-error"),
Text(err),
)
}
return g
}
func CSRF(r *ui.Request) Node {
return Input(
Type("hidden"),
Name("csrf"),
Value(r.CSRF),
)
}
func FormButton(color Color, label string) Node {
return Button(
Class("btn "+buttonColor(color)),
Text(label),
)
}
func ButtonLink(color Color, href, label string) Node {
return A(
Href(href),
Class("btn "+buttonColor(color)),
Text(label),
)
}
func buttonColor(color Color) string {
// Only colors being used are included so unused styles are not compiled.
switch color {
case ColorPrimary:
return "btn-primary"
case ColorInfo:
return "btn-info"
case ColorAccent:
return "btn-accent"
case ColorError:
return "btn-error"
case ColorLink:
return "btn-link"
default:
return ""
}
}