apps/jotts/tui/form_model.go 2.9 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
	formFieldTitle formField = iota
16
	formFieldContent
17
)
18
19
type formModel struct {
20
	title   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 = "Title"
47
	ti.Prompt = ""
48
	ti.CharLimit = 200
49
50
	ta := textarea.New()
51
	ta.Placeholder = "Write markdown..."
52
	ta.ShowLineNumbers = false
53
	ta.Prompt = ""
54
55
	return formModel{title: ti, content: ta, keys: defaultFormKeys()}
56
}
57
58
func (f *formModel) StartCreate() {
59
	f.shortID = ""
60
	f.isCreate = true
61
	f.title.SetValue("")
62
	f.content.SetValue("")
63
	f.field = formFieldTitle
64
	f.applyFocus()
65
}
66
67
func (f *formModel) StartEdit(n Note) {
68
	f.shortID = n.ShortID
69
	f.isCreate = false
70
	f.title.SetValue(n.Title)
71
	f.content.SetValue(n.Content)
72
	f.field = formFieldTitle
73
	f.applyFocus()
74
}
75
76
func (f *formModel) SetContent(s string) { f.content.SetValue(s) }
77
78
func (f *formModel) Blur() {
79
	f.title.Blur()
80
	f.content.Blur()
81
}
82
83
func (f *formModel) applyFocus() {
84
	switch f.field {
85
	case formFieldTitle:
86
		f.title.Focus()
87
		f.content.Blur()
88
	case formFieldContent:
89
		f.content.Focus()
90
		f.title.Blur()
91
	}
92
}
93
94
func (f *formModel) SetSize(w, h int) {
95
	f.title.SetWidth(max(w-4, 1))
96
	f.content.SetWidth(max(w-2, 1))
97
	f.content.SetHeight(max(h-6, 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
			title := strings.TrimSpace(f.title.Value())
108
			if title == "" {
109
				return f, func() tea.Msg { return statusMsg{Text: "title required"} }
110
			}
111
			return f, func() tea.Msg {
112
				return submitFormMsg{ShortID: f.shortID, Title: title, Content: f.content.Value()}
113
			}
114
		case key.Matches(km, f.keys.SwitchField):
115
			if f.field == formFieldTitle {
116
				f.field = formFieldContent
117
			} else {
118
				f.field = formFieldTitle
119
			}
120
			f.applyFocus()
121
			return f, nil
122
		}
123
	}
124
125
	var cmd tea.Cmd
126
	switch f.field {
127
	case formFieldTitle:
128
		f.title, cmd = f.title.Update(msg)
129
	case formFieldContent:
130
		f.content, cmd = f.content.Update(msg)
131
	}
132
	return f, cmd
133
}
134
135
func (f formModel) ActiveField() formField { return f.field }
136
func (f formModel) IsCreate() bool         { return f.isCreate }