form

package
v0.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package form is a vertical form component: a sequence of Text, Select, and Confirm fields with tab/shift-tab focus cycling and a submit button.

Each field renders as a bordered, titled component (Text wraps pkg/input, Confirm wraps pkg/toggle, Select owns its own pane), so the field's Label sits on the border as a pane title. Focus is signalled by the border color flipping from BorderInactive to BorderActive — there's no "▸" prefix or inline label line.

Usage:

f := form.New(theme.Dark().Form().With([]form.Field{
    form.Text(form.TextOptions{Key: "name", Label: "Name"}),
    form.Select(form.SelectOptions{Key: "role", Label: "Role",
        Options: []string{"admin", "user"}}),
    form.Confirm(form.ConfirmOptions{Key: "agree", Label: "I agree"}),
}))

The form emits form.SubmittedMsg (with all values keyed by Field.Key) when the user presses enter on the submit button, and form.CancelledMsg on esc. The enclosing screen's IsCapturingKeys should mirror Model.IsCapturingKeys.

Index

Constants

This section is empty.

Variables

View Source
var ErrRequired = errors.New("required")

ErrRequired is what a Required field reports while it is empty, and what a Select with RequirePick reports until something is chosen.

Functions

This section is empty.

Types

type CancelledMsg

type CancelledMsg struct{}

CancelledMsg is emitted when the user presses esc.

type ConfirmOptions

type ConfirmOptions struct {
	Key, Label string
	Initial    bool

	// Validate reports why the answer is unacceptable, or nil. A yes/no
	// always has a value, so there is no Required — express "must accept"
	// here instead.
	Validate func(any) error
}

ConfirmOptions configures a Confirm field.

type Field

type Field interface {
	Key() string
	Update(tea.Msg) (Field, tea.Cmd)
	// View renders the field into the given rect. Fields that occupy a
	// fixed number of rows may ignore r.H and use their own. focused tells
	// the field whether it currently owns input — fields use it to flip
	// border colors and expand inline (e.g. Select shows all options when
	// focused).
	View(r geom.Rect, focused bool) string
	// Rect reports where the field last rendered, so the form can work out
	// which one a click landed in.
	Rect() geom.Rect
	// SetRect records that area. The form calls it after rendering, because
	// a field's height is only known once it has drawn — handing every
	// field the form's full height would overlap them all and make a click
	// resolve to whichever the search reached first.
	SetRect(geom.Rect)
	Value() any
	// Validate reports why the field's current value is unacceptable, or
	// nil when it is fine.
	Validate() error
	// SetError shows err on the field; nil clears it. The form calls this,
	// so a field never decides on its own when to complain.
	SetError(err error)
	Focus() tea.Cmd
	Blur()
	SetStyles(*Styles)
	// Help returns the keys this field responds to — typically delegates
	// to the embedded component's Help().
	Help() []key.Binding
}

Field is the contract each form entry satisfies. Use the Text, Select, and Confirm constructors — don't implement Field yourself unless you need a custom entry kind. The form stores fields by pointer and mutates them in place on Update/Focus/Blur.

func Confirm

func Confirm(o ConfirmOptions) Field

Confirm returns a yes/no field backed by pkg/toggle. Label becomes the toggle pane's title.

func Select

func Select(o SelectOptions) Field

Select returns a single-choice field backed by pkg/list. While focused, up/down move the cursor; Value returns the highlighted option. Label becomes the list pane's title. Long option lists scroll within Height.

func Text

func Text(opts TextOptions) Field

Text returns a single-line text field backed by pkg/input. Label becomes the input pane's title.

type InvalidMsg added in v0.18.1

type InvalidMsg struct{ Keys []string }

InvalidMsg is emitted when a submit is refused because one or more fields failed validation. Keys names them, in field order. The form has already flagged the fields and moved focus to the first offender — a screen can ignore this entirely, and only needs it to react (a log line, a count, a statusbar note).

type Model

type Model struct {
	// contains filtered or unexported fields
}

Model is the form component.

func New

func New(opts Options) Model

New constructs a Form. Focuses the first field.

func (Model) Bool

func (m Model) Bool(key string) bool

Bool returns a field's value as a bool, or false if the field is missing or the value isn't a bool.

func (Model) FieldRect added in v0.18.1

func (m Model) FieldRect(i int) geom.Rect

FieldRect returns where field i last rendered. Empty before the first View, and for an out-of-range index.

func (Model) FocusedIndex added in v0.18.1

func (m Model) FocusedIndex() int

FocusedIndex returns which field currently has the keyboard; len(Fields) means the submit button.

func (Model) Help

func (m Model) Help() []key.Binding

Help returns the form's own bindings plus the focused field's. The enclosing screen typically returns these directly from its own Help().

func (Model) Init

func (m Model) Init() tea.Cmd

Init returns the textinput cursor-blink command so text fields animate.

func (Model) IsCapturingKeys

func (m Model) IsCapturingKeys() bool

IsCapturingKeys always returns true while the form is active — use it from the enclosing screen's IsCapturingKeys() so the app shell keeps its global keys (q / t / esc) out of the form.

func (Model) Rect added in v0.18.0

func (m Model) Rect() geom.Rect

Rect returns the rect the form was last placed at.

func (*Model) SetRect added in v0.18.0

func (m *Model) SetRect(r geom.Rect)

SetRect satisfies layout.Sizer so the form can be placed into a layout tree via layout.Sized(&formModel). The rect is retained so View can hand each field its own absolute position as it stacks them.

func (*Model) SetStyles

func (m *Model) SetStyles(s Styles)

SetStyles replaces the form's styles and propagates them to every field.

func (Model) String

func (m Model) String(key string) string

String returns a field's value as a string, or "" if the field is missing or the value isn't a string.

func (Model) SubmitRect added in v0.18.1

func (m Model) SubmitRect() geom.Rect

SubmitRect returns where the submit button last rendered. Empty before the first View.

func (Model) Update

func (m Model) Update(msg tea.Msg) (Model, tea.Cmd)

Update handles tab/shift-tab focus cycling, enter, esc, and forwards everything else to the focused field.

func (Model) Value

func (m Model) Value(key string) any

Value returns a single field's value by key, or nil if no such field.

func (Model) Values

func (m Model) Values() map[string]any

Values returns every field's value keyed by Field.Key.

func (Model) View

func (m Model) View() string

View renders the form vertically: each field, FieldSpacing blank lines between, then the submit button.

type Options

type Options struct {
	Width, Height int
	Fields        []Field
	// SubmitText is the label on the submit button. Defaults to "Submit".
	SubmitText string
	// FieldSpacing is the number of blank lines between adjacent fields and
	// before the submit button. Defaults to 0 (borders touch). Set higher
	// for a looser layout.
	FieldSpacing *int
	Styles       Styles
}

Options configures a new Form. All fields are optional except Fields.

func (Options) With

func (opts Options) With(fields []Field) Options

With returns a copy of opts with Fields replaced — handy when chaining from a theme builder: `theme.Dark().Form().With([]Field{…})`.

type SelectOptions

type SelectOptions struct {
	Key, Label string
	Options    []string
	Initial    int // initial cursor index
	// Height is the total field height including borders. Defaults to
	// min(len(Options)+2, 6) — i.e. auto-fit up to 4 visible rows; longer
	// option lists scroll within the fixed height.
	Height int

	// RequirePick starts the select with nothing highlighted and rejects
	// submission until the user chooses. A select always has a cursor
	// otherwise, so "required" for it means "must be picked deliberately"
	// rather than "must be non-empty". The label gains a "*".
	RequirePick bool
	// Validate reports why the chosen option is unacceptable, or nil.
	Validate func(any) error
}

SelectOptions configures a Select field.

type Styles

type Styles struct {
	// Input styles the text inside text fields (mapped to input.TextStyle).
	Input lipgloss.Style
	// Placeholder styles the placeholder text inside empty text fields.
	Placeholder lipgloss.Style
	// CursorColor is the foreground for the text-input cursor glyph.
	CursorColor lipgloss.TerminalColor

	// Selected styles the active item in Select and the chosen side of Confirm.
	Selected lipgloss.Style

	// PaneActive / PaneInactive color the field's border. Forwarded to each
	// field's pane (input.SetActiveColor / SetInactiveColor or pane defaults).
	PaneActive   lipgloss.TerminalColor
	PaneInactive lipgloss.TerminalColor

	// ErrorColor tints a field's border while it is showing a validation
	// error, regardless of focus — an invalid field is invalid whether or
	// not you are standing on it.
	ErrorColor lipgloss.TerminalColor
	// ErrorText styles the message rendered on the field's border.
	ErrorText lipgloss.Style

	// Submit and SubmitActive style the submit button (unfocused / focused).
	Submit       lipgloss.Style
	SubmitActive lipgloss.Style
}

Styles bundles the visual knobs the form passes down to its fields and uses for the submit button. Populate via theme.Form() or set directly on Options.

type SubmittedMsg

type SubmittedMsg struct{ Values map[string]any }

SubmittedMsg is emitted on enter over the submit button. Values maps each field's Key to its Value() — type depends on the field (string for Text and Select, bool for Confirm).

type TextOptions

type TextOptions struct {
	Key, Label, Placeholder, Initial string

	// Required rejects an empty value. The label gains a "*" so the
	// obligation is visible before anyone submits.
	Required bool
	// Validate reports why the value is unacceptable, or nil. Runs after
	// Required, so a blank required field says "required" rather than
	// whatever a format check would say about "".
	Validate func(any) error
}

TextOptions configures a Text field.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL