Search Apps Documentation Source Content File Folder Download Copy Actions Download

mdform.gno

5.15 Kb ยท 198 lines
  1package mdform
  2
  3import (
  4	"html"
  5	"strings"
  6)
  7
  8const (
  9	InputTypeText     = "text"
 10	InputTypeNumber   = "number"
 11	InputTypeEmail    = "email"
 12	InputTypePhone    = "tel"
 13	InputTypePassword = "password"
 14	InputTypeRadio    = "radio"
 15	InputTypeCheckbox = "checkbox"
 16)
 17
 18var (
 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)
 24
 25// New creates a new form.
 26func New(attributes ...string) *Form {
 27	assertEvenAttributes(attributes)
 28
 29	form := &Form{}
 30	for i := 0; i < len(attributes); i += 2 {
 31		name, value := attributes[i], attributes[i+1]
 32
 33		assertIsValidAttribute(name, formAttributes)
 34
 35		form.attrs = append(form.attrs, formatAttribute(name, value))
 36	}
 37	return form
 38}
 39
 40// Form is a form that can be rendered to Gno-Flavored Markdown.
 41type Form struct {
 42	attrs  []string
 43	fields []string
 44}
 45
 46// 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	}
 54
 55	assertEvenAttributes(attributes)
 56
 57	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		}
 68
 69		assertIsValidAttribute(name, inputAttributes)
 70
 71		attrs = append(attrs, formatAttribute(name, value))
 72	}
 73
 74	f.fields = append(f.fields, "<gno-input "+strings.Join(attrs, " ")+" />")
 75	return f
 76}
 77
 78// 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}
 83
 84// 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}
 89
 90// 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	}
 97
 98	assertEvenAttributes(attributes)
 99
100	attrs := []string{formatAttribute("name", name)}
101	for i := 0; i < len(attributes); i += 2 {
102		name, value := attributes[i], attributes[i+1]
103
104		assertIsValidAttribute(name, textareaAttributes)
105
106		attrs = append(attrs, formatAttribute(name, value))
107	}
108
109	f.fields = append(f.fields, "<gno-textarea "+strings.Join(attrs, " ")+" />")
110	return f
111}
112
113// 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	}
120
121	assertEvenAttributes(attributes)
122
123	attrs := []string{
124		formatAttribute("name", name),
125		formatAttribute("value", value),
126	}
127
128	for i := 0; i < len(attributes); i += 2 {
129		name, value := attributes[i], attributes[i+1]
130
131		assertIsValidAttribute(name, selectAttributes)
132
133		attrs = append(attrs, formatAttribute(name, value))
134	}
135
136	f.fields = append(f.fields, "<gno-select "+strings.Join(attrs, " ")+" />")
137	return f
138}
139
140// 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 = " " + attrs
146	}
147
148	return "<gno-form" + attrs + ">\n" + fields + "\n</gno-form>\n"
149}
150
151func (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	}
156
157	assertEvenAttributes(attributes)
158
159	attrs := []string{
160		formatAttribute("type", typeName),
161		formatAttribute("name", name),
162		formatAttribute("value", value),
163	}
164
165	for i := 0; i < len(attributes); i += 2 {
166		name, value := attributes[i], attributes[i+1]
167		if name == "type" || name == "value" {
168			continue
169		}
170
171		assertIsValidAttribute(name, inputAttributes)
172
173		attrs = append(attrs, formatAttribute(name, value))
174	}
175
176	f.fields = append(f.fields, "<gno-input "+strings.Join(attrs, " ")+" />")
177	return f
178}
179
180func formatAttribute(name, value string) string {
181	return name + `="` + html.EscapeString(value) + `"`
182}
183
184func assertEvenAttributes(attrs []string) {
185	if len(attrs)%2 != 0 {
186		panic("expected an even number of attribute arguments")
187	}
188}
189
190func assertIsValidAttribute(attr string, attrs []string) {
191	for _, name := range attrs {
192		if name == attr {
193			return
194		}
195	}
196
197	panic("invalid attribute: " + attr)
198}