Skip to content

Commit f1dfd4d

Browse files
authored
feat: Refactor Part3 (#7)
* feat: Refactor Part3 * feat: Refactor Part3
1 parent 38b741a commit f1dfd4d

File tree

6 files changed

+95
-24
lines changed

6 files changed

+95
-24
lines changed

internal/render/template.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type Template struct {
4444
}
4545

4646
type model struct {
47+
t string
4748
name string
4849
model ui.Model
4950
}
@@ -69,10 +70,21 @@ func (t *Template) Run() ([]byte, error) {
6970

7071
values := map[string]interface{}{}
7172
for _, v := range t.models {
72-
if _, err := tea.NewProgram(v.model).Run(); err != nil {
73+
if _, err = tea.NewProgram(v.model).Run(); err != nil {
7374
return nil, err
7475
}
75-
values[v.name] = v.model.Value()
76+
if v.model.Canceled() {
77+
return nil, errors.New("canceled")
78+
}
79+
val := v.model.Value()
80+
// hardcode for the select options
81+
if v.t == TypeSelect {
82+
tokens := strings.Split(val, ":")
83+
if len(tokens) > 0 {
84+
val = tokens[0]
85+
}
86+
}
87+
values[v.name] = val
7688
}
7789

7890
tmpl, err := template.New("cz").Parse(t.Format)
@@ -114,9 +126,10 @@ func (t *Template) init() error {
114126
case TypeTextArea:
115127
m = t.createTextAreaItem(item.Name, item.Desc, item.Required)
116128
default:
117-
return errors.New("unsupported item.type")
129+
return fmt.Errorf("unsupported type: %s", item.Type)
118130
}
119131
t.models = append(t.models, model{
132+
t: item.Type,
120133
name: item.Name,
121134
model: m,
122135
})
@@ -138,15 +151,15 @@ func (t *Template) createInputItem(name, label string, required bool) *ui.InputM
138151
if required {
139152
m.WithValidateFunc(NotBlankValidator(name))
140153
}
141-
return ui.NewInput(label)
154+
return m
142155
}
143156

144157
func (t *Template) createTextAreaItem(name, label string, required bool) *ui.TextAreaModel {
145158
m := ui.NewTextArea(label)
146159
if required {
147160
m.WithValidateFunc(NotBlankValidator(name))
148161
}
149-
return ui.NewTextArea(label)
162+
return m
150163
}
151164

152165
// NotBlankValidator is a verification function that checks whether the input is empty

internal/ui/help.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ui
2+
3+
import "github.com/charmbracelet/bubbles/key"
4+
5+
// keyMap defines a set of keybindings. To work for help it must satisfy
6+
// key.Map. It could also very easily be a map[string]key.Binding.
7+
type keyMap struct {
8+
Save key.Binding
9+
Quit key.Binding
10+
}
11+
12+
// ShortHelp returns keybindings to be shown in the mini help view. It's part
13+
// of the key.Map interface.
14+
func (k keyMap) ShortHelp() []key.Binding {
15+
return []key.Binding{k.Save, k.Quit}
16+
}
17+
18+
// FullHelp returns keybindings for the expanded help view. It's part of the
19+
// key.Map interface.
20+
func (k keyMap) FullHelp() [][]key.Binding {
21+
return [][]key.Binding{
22+
{k.Save, k.Quit}, // first column
23+
}
24+
}
25+
26+
var helpKeys = keyMap{
27+
Save: key.NewBinding(
28+
key.WithKeys("ctrl+q"),
29+
key.WithHelp("ctrl+q", "save"),
30+
),
31+
Quit: key.NewBinding(
32+
key.WithKeys("ctrl+c"),
33+
key.WithHelp("ctrl+c", "quit"),
34+
),
35+
}

internal/ui/select.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,12 @@ import (
1010
"github.com/charmbracelet/lipgloss"
1111
)
1212

13-
const listHeight = 14
14-
1513
var (
1614
titleStyle = lipgloss.NewStyle()
1715
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
1816
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
1917
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
2018
helpStyle = list.DefaultStyles().HelpStyle.PaddingBottom(1)
21-
quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 2)
2219
)
2320

2421
type Choices []Choice
@@ -67,6 +64,7 @@ type SelectModel struct {
6764
label string
6865
choice string
6966
finished bool
67+
canceled bool
7068
err error
7169

7270
list list.Model
@@ -109,6 +107,7 @@ func (m *SelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
109107
case tea.KeyMsg:
110108
switch keypress := tmsg.String(); keypress {
111109
case "q", "ctrl+c":
110+
m.canceled = true
112111
return m, tea.Quit
113112
case "enter":
114113
i, ok := m.list.SelectedItem().(Choice)
@@ -129,11 +128,20 @@ func (m *SelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
129128

130129
func (m *SelectModel) View() string {
131130
if m.choice != "" {
132-
return quitTextStyle.Render(fmt.Sprintf("%s\n%s", m.label, m.choice))
131+
return fmt.Sprintf(
132+
"%s %s\n%s\n",
133+
FontColor(DefaultValidateOkPrefix, colorValidateOk),
134+
m.label,
135+
quitValueStyle.Render(fmt.Sprintf(m.Value())),
136+
)
133137
}
134138
return "\n" + m.list.View()
135139
}
136140

137141
func (m *SelectModel) Value() string {
138142
return m.choice
139143
}
144+
145+
func (m *SelectModel) Canceled() bool {
146+
return m.canceled
147+
}

internal/ui/textarea.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ package ui
22

33
import (
44
"fmt"
5-
5+
"github.com/charmbracelet/bubbles/help"
6+
"github.com/charmbracelet/bubbles/key"
67
"github.com/charmbracelet/bubbles/textarea"
78
tea "github.com/charmbracelet/bubbletea"
9+
"github.com/charmbracelet/lipgloss"
810
)
911

12+
var quitValueStyle = lipgloss.NewStyle().Margin(0, 0, 0, 2)
13+
1014
type TextAreaModel struct {
1115
label string
1216
canceled bool
@@ -25,7 +29,9 @@ type TextAreaModel struct {
2529
// validateErrPrefix is the prompt prefix when the verification is successful
2630
validateErrPrefix string
2731

28-
input textarea.Model
32+
helpKeys keyMap
33+
help help.Model
34+
input textarea.Model
2935
}
3036

3137
func NewTextArea(label string) *TextAreaModel {
@@ -37,6 +43,8 @@ func NewTextArea(label string) *TextAreaModel {
3743
return &TextAreaModel{
3844
input: ti,
3945
label: label,
46+
helpKeys: helpKeys,
47+
help: help.New(),
4048
validateFunc: DefaultValidateFunc,
4149
validateOkPrefix: DefaultValidateOkPrefix,
4250
validateErrPrefix: DefaultValidateErrPrefix,
@@ -90,13 +98,13 @@ func (m *TextAreaModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9098
var cmd tea.Cmd
9199

92100
switch tmsg := msg.(type) {
101+
case tea.WindowSizeMsg:
102+
// If we set a width on the help menu it can gracefully truncate
103+
// its view as needed.
104+
m.help.Width = tmsg.Width
93105
case tea.KeyMsg:
94-
switch tmsg.Type {
95-
case tea.KeyEsc:
96-
if m.input.Focused() {
97-
m.input.Blur()
98-
}
99-
case tea.KeyCtrlJ:
106+
switch {
107+
case key.Matches(tmsg, m.helpKeys.Save):
100108
// If the real-time verification function does not return an error,
101109
// then the input has been completed
102110
if m.err == nil {
@@ -105,10 +113,14 @@ func (m *TextAreaModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
105113
}
106114
// If there is a verification error, the error message should be display
107115
m.showErr = true
108-
case tea.KeyCtrlC:
116+
case key.Matches(tmsg, m.helpKeys.Quit):
109117
m.canceled = true
110118
return m, tea.Quit
111-
case tea.KeyRunes:
119+
case tmsg.Type == tea.KeyEsc:
120+
if m.input.Focused() {
121+
m.input.Blur()
122+
}
123+
case tmsg.Type == tea.KeyRunes:
112124
// Hide verification failure message when entering content again
113125
m.showErr = false
114126
m.err = nil
@@ -133,7 +145,7 @@ func (m *TextAreaModel) View() string {
133145
"%s %s\n%s\n",
134146
FontColor(m.validateOkPrefix, colorValidateOk),
135147
m.label,
136-
m.Value(),
148+
quitValueStyle.Render(fmt.Sprintf(m.Value())),
137149
)
138150
}
139151

@@ -143,6 +155,7 @@ func (m *TextAreaModel) View() string {
143155
}
144156

145157
var showMsg, errMsg string
158+
helpView := m.help.View(m.helpKeys)
146159
if m.err != nil {
147160
showMsg = fmt.Sprintf(
148161
"%s %s\n%s",
@@ -156,10 +169,11 @@ func (m *TextAreaModel) View() string {
156169
}
157170
} else {
158171
showMsg = fmt.Sprintf(
159-
"%s %s\n%s",
172+
"%s %s\n%s\n%s",
160173
FontColor(m.validateOkPrefix, colorValidateOk),
161174
m.label,
162175
m.input.View(),
176+
helpStyle.Render(helpView),
163177
)
164178
}
165179

internal/ui/textinput.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func (m *InputModel) View() string {
126126
"%s %s\n%s\n",
127127
FontColor(m.validateOkPrefix, colorValidateOk),
128128
m.label,
129-
m.Value(),
129+
quitValueStyle.Render(m.Value()),
130130
)
131131
case textinput.EchoNone:
132132
return fmt.Sprintf(
@@ -139,7 +139,7 @@ func (m *InputModel) View() string {
139139
"%s %s\n%s\n",
140140
FontColor(m.validateOkPrefix, colorValidateOk),
141141
m.label,
142-
GenMask(len([]rune(m.Value()))),
142+
quitValueStyle.Render(GenMask(len([]rune(m.Value())))),
143143
)
144144
}
145145
}

internal/ui/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type Model interface {
1010
tea.Model
1111

1212
Value() string
13+
Canceled() bool
1314
}
1415

1516
// EchoMode sets the input behavior of the text input field.
@@ -33,7 +34,7 @@ const (
3334
DefaultValidateOkPrefix = "✔"
3435
DefaultValidateErrPrefix = "✘"
3536
DefaultTextAreaMaxHeight = 5
36-
DefaultTextAreaHeight = 5
37+
DefaultTextAreaHeight = 2
3738
DefaultSelectWidth = 20
3839
DefaultSelectHeight = 12
3940

0 commit comments

Comments
 (0)