Form¶
pkg/form is a vertical form: text, password, select and confirm fields, each
a bordered component with its label on the border, then a submit button. It
owns focus cycling and validation.
┌ Name * ──────────────────────────────┐
│ │
└──────────────────────────────────────┘
┌ Password ────────────────────────────┐
│••••••• │
└──────────────────────────────────────┘
┌ Role ────────────────────────────────┐
│▸ admin │
│ user │
│ viewer │
└──────────────────────────────────────┘
┌ Send welcome email? ─────────────────┐
│[ yes ] [ no ] │
└──────────────────────────────────────┘
[ Submit ]
Usage¶
s.form = form.New(t.Form().With([]form.Field{
form.Text(form.TextOptions{Key: "name", Label: "Name", Required: true}),
form.Text(form.TextOptions{Key: "email", Label: "Email", Validate: validEmail}),
form.Password(form.PasswordOptions{Key: "pass", Label: "Password", Required: true}),
form.Select(form.SelectOptions{Key: "role", Label: "Role",
Options: []string{"admin", "user", "viewer"}, RequirePick: true}),
form.Confirm(form.ConfirmOptions{Key: "welcome", Label: "Send welcome email?"}),
}))
func (s *Screen) Init() tea.Cmd { return s.form.Init() } // cursor blink
func (s *Screen) IsCapturingKeys() bool { return s.form.IsCapturingKeys() }
func (s *Screen) Update(msg tea.Msg) (screen.Screen, tea.Cmd) {
switch m := msg.(type) {
case form.SubmittedMsg:
return s, s.createUser(m.Values["name"].(string), s.form.String("role"))
case form.CancelledMsg:
return s, screen.Pop(nil)
}
var cmd tea.Cmd
s.form, cmd = s.form.Update(msg)
return s, cmd
}
Because a form always captures keys, the shell's esc-pop is suppressed;
handle form.CancelledMsg to leave.
Fields¶
| Constructor | Value type | Options |
|---|---|---|
form.Text(TextOptions) |
string |
Key, Label, Placeholder, Initial, Required, Validate |
form.Password(PasswordOptions) |
string |
as Text, plus MaskChar |
form.Select(SelectOptions) |
string (the highlighted option) |
Key, Label, Options, Initial (index), Height (default fits up to 4 rows), RequirePick, Validate |
form.Confirm(ConfirmOptions) |
bool |
Key, Label, Initial, Validate |
Validate is func(any) error, receiving the field's value. Implement
form.Field yourself only for a genuinely new kind of entry.
Validation¶
Validation belongs to the form, not the screen:
Requiredrejects an empty value and adds*to the label.RequirePickstarts a select with nothing highlighted and rejects submission until the user picks one. (A select always has a value otherwise.)Validateruns afterRequired, so a blank required field says "required" rather than a format error.- Validation runs on submit, then live for fields already flagged.
- An invalid submit is refused: no
SubmittedMsg, focus moves to the first offender, andform.InvalidMsg{Keys}lets the screen react if it wants to. - Errors render on the field's own border (a tint plus a short message in the top-right slot), so nothing reflows while the user corrects it.
Cross-field rules (password confirmation, date ranges) aren't supported:
compare values in the screen on SubmittedMsg.
Options¶
| Field | Type | Default | Meaning |
|---|---|---|---|
Fields |
[]form.Field |
required | The fields, top to bottom. |
SubmitText |
string |
"Submit" |
Submit button label. |
FieldSpacing |
*int |
0 | Blank lines between fields. |
Styles |
form.Styles |
theme | Input, placeholder, cursor, selected, pane colors and border shapes, error color and text, submit button styles. |
Width, Height |
int |
Set by the layout engine. |
opts.With(fields) returns a copy with Fields replaced, for chaining from
t.Form().
Keys¶
| Key | Action |
|---|---|
tab / shift+tab |
next / previous field |
enter |
on the submit button: submit |
esc |
cancel (CancelledMsg) |
| field keys | typing, ↑/↓ in a select, ←/→/y/n in a confirm |
Mouse: clicking a field moves focus to it; clicking submit submits.
Methods¶
| Method | Does |
|---|---|
Values() map[string]any |
Every value by key. |
Value(key), String(key), Bool(key) |
One value, typed. |
FocusedIndex() |
Which field has the keyboard (len(Fields) = submit). |
SetStyles(s) |
Restyle every field. |
FieldRect(i), SubmitRect() |
Geometry. |
IsCapturingKeys() |
Always true while the form is active. |
Messages¶
| Message | When |
|---|---|
form.SubmittedMsg{Values} |
submit with every field valid |
form.InvalidMsg{Keys} |
submit refused; Keys names the invalid fields |
form.CancelledMsg{} |
esc |
See examples/components/form.