package main
import (
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
// form is a tiny multi-field text input flow: tab/down/up move focus,
// enter on the last field submits, esc cancels.
type form struct {
title string
labels []string
inputs []textinput.Model
focus int
onSubmit func(values []string) tea.Msg
onCancel func() tea.Msg
}
func newForm(title string, labels []string, placeholders []string, onSubmit func([]string) tea.Msg, onCancel func() tea.Msg) *form {
inputs := make([]textinput.Model, len(labels))
for i := range inputs {
ti := textinput.New()
if i < len(placeholders) {
ti.Placeholder = placeholders[i]
}
ti.CharLimit = 256
ti.Width = 60
if i == 0 {
ti.Focus()
}
inputs[i] = ti
}
return &form{title: title, labels: labels, inputs: inputs, onSubmit: onSubmit, onCancel: onCancel}
}
func (f *form) Update(msg tea.Msg) (*form, tea.Cmd) {
if keyMsg, ok := msg.(tea.KeyMsg); ok {
switch keyMsg.String() {
case "esc":
return f, func() tea.Msg { return f.onCancel() }
case "tab", "down":
f.inputs[f.focus].Blur()
f.focus = (f.focus + 1) % len(f.inputs)
f.inputs[f.focus].Focus()
return f, nil
case "shift+tab", "up":
f.inputs[f.focus].Blur()
f.focus = (f.focus - 1 + len(f.inputs)) % len(f.inputs)
f.inputs[f.focus].Focus()
return f, nil
case "enter":
if f.focus == len(f.inputs)-1 {
values := make([]string, len(f.inputs))
for i, in := range f.inputs {
values[i] = in.Value()
}
return f, func() tea.Msg { return f.onSubmit(values) }
}
f.inputs[f.focus].Blur()
f.focus++
f.inputs[f.focus].Focus()
return f, nil
}
}
var cmd tea.Cmd
f.inputs[f.focus], cmd = f.inputs[f.focus].Update(msg)
return f, cmd
}
var (
formTitleStyle = lipgloss.NewStyle().Bold(true).MarginBottom(1)
formLabelStyle = lipgloss.NewStyle().Width(14)
formHintStyle = lipgloss.NewStyle().Faint(true).MarginTop(1)
)
func (f *form) View() string {
s := formTitleStyle.Render(f.title) + "\n"
for i, in := range f.inputs {
s += formLabelStyle.Render(f.labels[i]+":") + in.View() + "\n"
}
s += formHintStyle.Render("tab/↑↓ move · enter next/submit · esc cancel")
return s
}