1package mdform23import (4 "html"5 "strings"6)78const (9 InputTypeText = "text"10 InputTypeNumber = "number"11 InputTypeEmail = "email"12 InputTypePhone = "tel"13 InputTypePassword = "password"14 InputTypeRadio = "radio"15 InputTypeCheckbox = "checkbox"16)1718var (19 formAttributes = []string{"exec", "path"}20 inputAttributes = []string{"checked", "description", "placeholder", "readonly", "required", "type", "value"}21 textareaAttributes = []string{"placeholder", "readonly", "required", "rows", "value"}22 selectAttributes = []string{"description", "readonly", "required", "selected"}23)2425// New creates a new form.26func New(attributes ...string) *Form {27 assertEvenAttributes(attributes)2829 form := &Form{}30 for i := 0; i < len(attributes); i += 2 {31 name, value := attributes[i], attributes[i+1]3233 assertIsValidAttribute(name, formAttributes)3435 form.attrs = append(form.attrs, formatAttribute(name, value))36 }37 return form38}3940// Form is a form that can be rendered to Gno-Flavored Markdown.41type Form struct {42 attrs []string43 fields []string44}4546// Input appends a new input to form fields.47// Use `Form.Radio()` or `Form.Checkbox()` to append those types of inputs to the form.48// Method panics when appending inputs of type radio or checkbox, or when attributes are not valid.49func (f *Form) Input(name string, attributes ...string) *Form {50 name = strings.TrimSpace(name)51 if name == "" {52 panic("form input name is required")53 }5455 assertEvenAttributes(attributes)5657 attrs := []string{formatAttribute("name", name)}58 for i := 0; i < len(attributes); i += 2 {59 name, value := attributes[i], attributes[i+1]60 if name == "type" {61 switch value {62 case InputTypeRadio:63 panic("use form.Radio() to create inputs of type radio")64 case InputTypeCheckbox:65 panic("use form.Checkbox() to create inputs of type checkbox")66 }67 }6869 assertIsValidAttribute(name, inputAttributes)7071 attrs = append(attrs, formatAttribute(name, value))72 }7374 f.fields = append(f.fields, "<gno-input "+strings.Join(attrs, " ")+" />")75 return f76}7778// Radio appends a new input of type radio to form fields.79// Method panics when attributes are not valid.80func (f *Form) Radio(name, value string, attributes ...string) *Form {81 return f.appendInputType(InputTypeRadio, name, value, attributes...)82}8384// Checkbox appends a new input of type checkbox to form fields.85// Method panics when attributes are not valid.86func (f *Form) Checkbox(name, value string, attributes ...string) *Form {87 return f.appendInputType(InputTypeCheckbox, name, value, attributes...)88}8990// Textarea appends a new textarea to form fields.91// Method panics when attributes are not valid.92func (f *Form) Textarea(name string, attributes ...string) *Form {93 name = strings.TrimSpace(name)94 if name == "" {95 panic("form textarea name is required")96 }9798 assertEvenAttributes(attributes)99100 attrs := []string{formatAttribute("name", name)}101 for i := 0; i < len(attributes); i += 2 {102 name, value := attributes[i], attributes[i+1]103104 assertIsValidAttribute(name, textareaAttributes)105106 attrs = append(attrs, formatAttribute(name, value))107 }108109 f.fields = append(f.fields, "<gno-textarea "+strings.Join(attrs, " ")+" />")110 return f111}112113// Select appends a new select to form fields.114// Method panics when attributes are not valid.115func (f *Form) Select(name, value string, attributes ...string) *Form {116 name = strings.TrimSpace(name)117 if name == "" {118 panic("form select name is required")119 }120121 assertEvenAttributes(attributes)122123 attrs := []string{124 formatAttribute("name", name),125 formatAttribute("value", value),126 }127128 for i := 0; i < len(attributes); i += 2 {129 name, value := attributes[i], attributes[i+1]130131 assertIsValidAttribute(name, selectAttributes)132133 attrs = append(attrs, formatAttribute(name, value))134 }135136 f.fields = append(f.fields, "<gno-select "+strings.Join(attrs, " ")+" />")137 return f138}139140// String returns the form as Gno-Flavored Markdown.141func (f Form) String() string {142 fields := strings.Join(f.fields, "\n")143 attrs := strings.Join(f.attrs, " ")144 if len(attrs) > 0 {145 attrs = " " + attrs146 }147148 return "<gno-form" + attrs + ">\n" + fields + "\n</gno-form>\n"149}150151func (f *Form) appendInputType(typeName, name, value string, attributes ...string) *Form {152 name = strings.TrimSpace(name)153 if name == "" {154 panic("form " + typeName + " input name is required")155 }156157 assertEvenAttributes(attributes)158159 attrs := []string{160 formatAttribute("type", typeName),161 formatAttribute("name", name),162 formatAttribute("value", value),163 }164165 for i := 0; i < len(attributes); i += 2 {166 name, value := attributes[i], attributes[i+1]167 if name == "type" || name == "value" {168 continue169 }170171 assertIsValidAttribute(name, inputAttributes)172173 attrs = append(attrs, formatAttribute(name, value))174 }175176 f.fields = append(f.fields, "<gno-input "+strings.Join(attrs, " ")+" />")177 return f178}179180func formatAttribute(name, value string) string {181 return name + `="` + html.EscapeString(value) + `"`182}183184func assertEvenAttributes(attrs []string) {185 if len(attrs)%2 != 0 {186 panic("expected an even number of attribute arguments")187 }188}189190func assertIsValidAttribute(attr string, attrs []string) {191 for _, name := range attrs {192 if name == attr {193 return194 }195 }196197 panic("invalid attribute: " + attr)198}199Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.