1package ui
2
3import (
4 "github.com/charmbracelet/bubbles/v2/key"
5 tea "github.com/charmbracelet/bubbletea/v2"
6 "github.com/charmbracelet/lipgloss/v2"
7)
8
9// QuitDialogKeyMap represents key bindings for the quit dialog.
10type QuitDialogKeyMap struct {
11 LeftRight,
12 EnterSpace,
13 Yes,
14 No,
15 Tab,
16 Close key.Binding
17}
18
19// DefaultQuitKeyMap returns the default key bindings for the quit dialog.
20func DefaultQuitKeyMap() QuitDialogKeyMap {
21 return QuitDialogKeyMap{
22 LeftRight: key.NewBinding(
23 key.WithKeys("left", "right"),
24 key.WithHelp("←/→", "switch options"),
25 ),
26 EnterSpace: key.NewBinding(
27 key.WithKeys("enter", " "),
28 key.WithHelp("enter/space", "confirm"),
29 ),
30 Yes: key.NewBinding(
31 key.WithKeys("y", "Y", "ctrl+c"),
32 key.WithHelp("y/Y/ctrl+c", "yes"),
33 ),
34 No: key.NewBinding(
35 key.WithKeys("n", "N"),
36 key.WithHelp("n/N", "no"),
37 ),
38 Tab: key.NewBinding(
39 key.WithKeys("tab"),
40 key.WithHelp("tab", "switch options"),
41 ),
42 Close: key.NewBinding(
43 key.WithKeys("esc", "alt+esc"),
44 key.WithHelp("esc", "cancel"),
45 ),
46 }
47}
48
49// QuitDialog represents a confirmation dialog for quitting the application.
50type QuitDialog struct {
51 com *Common
52 keyMap QuitDialogKeyMap
53 selectedNo bool // true if "No" button is selected
54}
55
56// NewQuitDialog creates a new quit confirmation dialog.
57func NewQuitDialog(com *Common) *QuitDialog {
58 q := &QuitDialog{
59 com: com,
60 keyMap: DefaultQuitKeyMap(),
61 }
62 return q
63}
64
65// ID implements [Model].
66func (*QuitDialog) ID() string {
67 return "quit"
68}
69
70// Update implements [Model].
71func (q *QuitDialog) Update(msg tea.Msg) (Dialog, tea.Cmd) {
72 switch msg := msg.(type) {
73 case tea.KeyPressMsg:
74 switch {
75 case key.Matches(msg, q.keyMap.LeftRight, q.keyMap.Tab):
76 q.selectedNo = !q.selectedNo
77 return q, nil
78 case key.Matches(msg, q.keyMap.EnterSpace):
79 if !q.selectedNo {
80 return q, tea.Quit
81 }
82 return nil, nil
83 case key.Matches(msg, q.keyMap.Yes):
84 return q, tea.Quit
85 case key.Matches(msg, q.keyMap.No, q.keyMap.Close):
86 return nil, nil
87 }
88 }
89
90 return q, nil
91}
92
93// View implements [Model].
94func (q *QuitDialog) View() string {
95 const question = "Are you sure you want to quit?"
96 baseStyle := q.com.Styles.Base
97 yesStyle := q.com.Styles.ButtonSelected
98 noStyle := q.com.Styles.ButtonUnselected
99
100 if q.selectedNo {
101 noStyle = q.com.Styles.ButtonSelected
102 yesStyle = q.com.Styles.ButtonUnselected
103 }
104
105 const horizontalPadding = 3
106 yesButton := yesStyle.PaddingLeft(horizontalPadding).Underline(true).Render("Y") +
107 yesStyle.PaddingRight(horizontalPadding).Render("ep!")
108 noButton := noStyle.PaddingLeft(horizontalPadding).Underline(true).Render("N") +
109 noStyle.PaddingRight(horizontalPadding).Render("ope")
110
111 buttons := baseStyle.Width(lipgloss.Width(question)).Align(lipgloss.Right).Render(
112 lipgloss.JoinHorizontal(lipgloss.Center, yesButton, " ", noButton),
113 )
114
115 content := baseStyle.Render(
116 lipgloss.JoinVertical(
117 lipgloss.Center,
118 question,
119 "",
120 buttons,
121 ),
122 )
123
124 return q.com.Styles.BorderFocus.Render(content)
125}
126
127// ShortHelp implements [help.KeyMap].
128func (q *QuitDialog) ShortHelp() []key.Binding {
129 return []key.Binding{
130 q.keyMap.LeftRight,
131 q.keyMap.EnterSpace,
132 }
133}
134
135// FullHelp implements [help.KeyMap].
136func (q *QuitDialog) FullHelp() [][]key.Binding {
137 return [][]key.Binding{
138 {q.keyMap.LeftRight, q.keyMap.EnterSpace, q.keyMap.Yes, q.keyMap.No},
139 {q.keyMap.Tab, q.keyMap.Close},
140 }
141}