apps/sipp/tui/form_model.go 3.0 K raw
1
package tui
2
3
import (
4
	"strings"
5
6
	"charm.land/bubbles/v2/key"
7
	"charm.land/bubbles/v2/textarea"
8
	"charm.land/bubbles/v2/textinput"
9
	tea "charm.land/bubbletea/v2"
10
)
11
12
type formField uint8
13
14
const (
15
	formFieldName formField = iota
16
	formFieldContent
17
)
18
19
type formModel struct {
20
	name    textinput.Model
21
	content textarea.Model
22
23
	field    formField
24
	shortID  string
25
	isCreate bool
26
27
	keys formKeys
28
}
29
30
type formKeys struct {
31
	Save        key.Binding
32
	Cancel      key.Binding
33
	SwitchField key.Binding
34
}
35
36
func defaultFormKeys() formKeys {
37
	return formKeys{
38
		Save:        key.NewBinding(key.WithKeys("ctrl+s"), key.WithHelp("⌃s", "save")),
39
		Cancel:      key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "cancel")),
40
		SwitchField: key.NewBinding(key.WithKeys("tab"), key.WithHelp("⇥", "switch field")),
41
	}
42
}
43
44
func newFormModel() formModel {
45
	ti := textinput.New()
46
	ti.Placeholder = "name.ext"
47
	ti.Prompt = ""
48
	ti.CharLimit = 200
49
50
	ta := textarea.New()
51
	ta.Placeholder = "Paste code..."
52
	ta.ShowLineNumbers = true
53
	ta.Prompt = ""
54
55
	return formModel{name: ti, content: ta, keys: defaultFormKeys()}
56
}
57
58
func (f *formModel) StartCreate() {
59
	f.shortID = ""
60
	f.isCreate = true
61
	f.name.SetValue("")
62
	f.content.SetValue("")
63
	f.field = formFieldName
64
	f.applyFocus()
65
}
66
67
func (f *formModel) StartEdit(s Snippet) {
68
	f.shortID = s.ShortID
69
	f.isCreate = false
70
	f.name.SetValue(s.Name)
71
	f.content.SetValue(s.Content)
72
	f.field = formFieldName
73
	f.applyFocus()
74
}
75
76
func (f *formModel) SetContent(s string) { f.content.SetValue(s) }
77
78
func (f *formModel) Blur() {
79
	f.name.Blur()
80
	f.content.Blur()
81
}
82
83
func (f *formModel) applyFocus() {
84
	switch f.field {
85
	case formFieldName:
86
		f.name.Focus()
87
		f.content.Blur()
88
	case formFieldContent:
89
		f.content.Focus()
90
		f.name.Blur()
91
	}
92
}
93
94
func (f *formModel) SetSize(w, h int) {
95
	f.name.SetWidth(max(w-4, 1))
96
	f.content.SetWidth(max(w-2, 1))
97
	f.content.SetHeight(max(h-5, 1))
98
}
99
100
func (f formModel) Update(msg tea.Msg) (formModel, tea.Cmd) {
101
	if km, ok := msg.(tea.KeyPressMsg); ok {
102
		switch {
103
		case key.Matches(km, f.keys.Cancel):
104
			f.Blur()
105
			return f, func() tea.Msg { return cancelFormMsg{} }
106
		case key.Matches(km, f.keys.Save):
107
			name := strings.TrimSpace(f.name.Value())
108
			if name == "" {
109
				return f, func() tea.Msg { return statusMsg{Text: "name required"} }
110
			}
111
			content := f.content.Value()
112
			if strings.TrimSpace(content) == "" {
113
				return f, func() tea.Msg { return statusMsg{Text: "content required"} }
114
			}
115
			return f, func() tea.Msg {
116
				return submitFormMsg{ShortID: f.shortID, Name: name, Content: content}
117
			}
118
		case key.Matches(km, f.keys.SwitchField):
119
			if f.field == formFieldName {
120
				f.field = formFieldContent
121
			} else {
122
				f.field = formFieldName
123
			}
124
			f.applyFocus()
125
			return f, nil
126
		}
127
	}
128
129
	var cmd tea.Cmd
130
	switch f.field {
131
	case formFieldName:
132
		f.name, cmd = f.name.Update(msg)
133
	case formFieldContent:
134
		f.content, cmd = f.content.Update(msg)
135
	}
136
	return f, cmd
137
}
138
139
func (f formModel) ActiveField() formField { return f.field }
140
func (f formModel) IsCreate() bool         { return f.isCreate }