diff --git a/gno.land/pkg/gnoweb/frontend/css/06-blocks.css b/gno.land/pkg/gnoweb/frontend/css/06-blocks.css index a9c04ec243f..ad6dee9587c 100644 --- a/gno.land/pkg/gnoweb/frontend/css/06-blocks.css +++ b/gno.land/pkg/gnoweb/frontend/css/06-blocks.css @@ -2144,6 +2144,13 @@ pre.chroma-chroma { margin-inline-start: var(--g-space-1); } + .gno-form_required-badge { + font-size: var(--g-font-size-50); + color: var(--s-color-text-tertiary); + font-weight: var(--g-font-normal); + margin-inline-start: var(--g-space-1); + } + .gno-form_selectable { display: flex; column-gap: var(--g-space-2); diff --git a/gno.land/pkg/gnoweb/markdown/ext_forms.go b/gno.land/pkg/gnoweb/markdown/ext_forms.go index 6f7a60d2ea5..31465c9614a 100644 --- a/gno.land/pkg/gnoweb/markdown/ext_forms.go +++ b/gno.land/pkg/gnoweb/markdown/ext_forms.go @@ -69,6 +69,7 @@ type FormInput struct { Value string Checked bool Readonly bool + Required bool Description string Error error } @@ -91,6 +92,9 @@ func (e FormInput) String() string { if e.Readonly { s += " (readonly=true)" } + if e.Required { + s += " (required=true)" + } return s } @@ -101,6 +105,7 @@ type FormTextarea struct { Rows int Value string Readonly bool + Required bool Description string Error error } @@ -118,6 +123,9 @@ func (e FormTextarea) String() string { if e.Readonly { s += " (readonly=true)" } + if e.Required { + s += " (required=true)" + } return s } @@ -127,6 +135,7 @@ type FormSelect struct { Value string Selected bool Readonly bool + Required bool Description string Error error } @@ -144,6 +153,9 @@ func (e FormSelect) String() string { if e.Readonly { s += " (readonly=true)" } + if e.Required { + s += " (required=true)" + } return s } @@ -296,6 +308,7 @@ func (p *FormParser) parseInput(node *FormNode, tok html.Token) { input.Value = attrs["value"] input.Checked = attrs["checked"] == "true" input.Readonly = attrs["readonly"] == "true" + input.Required = attrs["required"] == "true" // Validate if err := node.validateName(input.Name, input.Type); err != nil { @@ -369,6 +382,8 @@ func (p *FormParser) parseTextarea(node *FormNode, tok html.Token) { textarea.Description = strings.TrimSpace(attr.Val) case "readonly": textarea.Readonly = strings.TrimSpace(attr.Val) == "true" + case "required": + textarea.Required = strings.TrimSpace(attr.Val) == "true" } } @@ -400,6 +415,8 @@ func (p *FormParser) parseSelect(node *FormNode, tok html.Token) { sel.Description = strings.TrimSpace(attr.Val) case "readonly": sel.Readonly = strings.TrimSpace(attr.Val) == "true" + case "required": + sel.Required = strings.TrimSpace(attr.Val) == "true" } } @@ -598,6 +615,9 @@ func (r *FormRenderer) renderInput(w util.BufWriter, e FormInput, idx int, lastD if e.Readonly { fmt.Fprint(w, ` disabled`) } + if e.Required { + fmt.Fprint(w, ` required`) + } if isExec { fmt.Fprintf(w, ` data-action-function-target="param-input" data-action="change->action-function#updateAllArgs" data-action-function-param-value="%s"`, HTMLEscapeString(e.Name)) } @@ -612,17 +632,25 @@ func (r *FormRenderer) renderInput(w util.BufWriter, e FormInput, idx int, lastD if e.Readonly { readonlyBadge = `(readonly)` } - fmt.Fprintf(w, ` + requiredBadge := "" + if e.Required { + requiredBadge = `*` + } + fmt.Fprintf(w, ` -`, HTMLEscapeString(uniqueID), HTMLEscapeString(label), readonlyBadge) +`, HTMLEscapeString(uniqueID), requiredBadge, HTMLEscapeString(label), readonlyBadge) } else { readonlyBadge := "" if e.Readonly { readonlyBadge = `(readonly)` } - fmt.Fprintf(w, `
+ requiredBadge := "" + if e.Required { + requiredBadge = `*` + } + fmt.Fprintf(w, `
(readonly)` } + requiredBadge := "" + if e.Required { + requiredBadge = `*` + } - fmt.Fprintf(w, `
+ fmt.Fprintf(w, `