quit.go

  1package dialog
  2
  3import (
  4	"charm.land/bubbles/v2/key"
  5	tea "charm.land/bubbletea/v2"
  6	"charm.land/lipgloss/v2"
  7	"github.com/charmbracelet/crush/internal/ui/common"
  8)
  9
 10// QuitID is the identifier for the quit dialog.
 11const QuitID = "quit"
 12
 13// Quit represents a confirmation dialog for quitting the application.
 14type Quit struct {
 15	com        *common.Common
 16	selectedNo bool // true if "No" button is selected
 17	keyMap     struct {
 18		LeftRight,
 19		EnterSpace,
 20		Yes,
 21		No,
 22		Tab,
 23		Close key.Binding
 24	}
 25}
 26
 27// NewQuit creates a new quit confirmation dialog.
 28func NewQuit(com *common.Common) *Quit {
 29	q := &Quit{
 30		com:        com,
 31		selectedNo: true,
 32	}
 33	q.keyMap.LeftRight = key.NewBinding(
 34		key.WithKeys("left", "right"),
 35		key.WithHelp("←/→", "switch options"),
 36	)
 37	q.keyMap.EnterSpace = key.NewBinding(
 38		key.WithKeys("enter", " "),
 39		key.WithHelp("enter/space", "confirm"),
 40	)
 41	q.keyMap.Yes = key.NewBinding(
 42		key.WithKeys("y", "Y", "ctrl+c"),
 43		key.WithHelp("y/Y/ctrl+c", "yes"),
 44	)
 45	q.keyMap.No = key.NewBinding(
 46		key.WithKeys("n", "N"),
 47		key.WithHelp("n/N", "no"),
 48	)
 49	q.keyMap.Tab = key.NewBinding(
 50		key.WithKeys("tab"),
 51		key.WithHelp("tab", "switch options"),
 52	)
 53	q.keyMap.Close = CloseKey
 54	return q
 55}
 56
 57// ID implements [Model].
 58func (*Quit) ID() string {
 59	return QuitID
 60}
 61
 62// Update implements [Model].
 63func (q *Quit) Update(msg tea.Msg) tea.Cmd {
 64	switch msg := msg.(type) {
 65	case tea.KeyPressMsg:
 66		switch {
 67		case key.Matches(msg, q.keyMap.LeftRight, q.keyMap.Tab):
 68			q.selectedNo = !q.selectedNo
 69			return nil
 70		case key.Matches(msg, q.keyMap.EnterSpace):
 71			if !q.selectedNo {
 72				return tea.Quit
 73			}
 74			return nil
 75		case key.Matches(msg, q.keyMap.Yes):
 76			return tea.Quit
 77		case key.Matches(msg, q.keyMap.No, q.keyMap.Close):
 78			return nil
 79		}
 80	}
 81
 82	return nil
 83}
 84
 85// View implements [Dialog].
 86func (q *Quit) View() string {
 87	const question = "Are you sure you want to quit?"
 88	baseStyle := q.com.Styles.Base
 89	buttonOpts := []common.ButtonOpts{
 90		{Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
 91		{Text: "Nope", Selected: q.selectedNo, Padding: 3},
 92	}
 93	buttons := common.ButtonGroup(q.com.Styles, buttonOpts, " ")
 94	content := baseStyle.Render(
 95		lipgloss.JoinVertical(
 96			lipgloss.Center,
 97			question,
 98			"",
 99			buttons,
100		),
101	)
102
103	return q.com.Styles.BorderFocus.Render(content)
104}
105
106// ShortHelp implements [help.KeyMap].
107func (q *Quit) ShortHelp() []key.Binding {
108	return []key.Binding{
109		q.keyMap.LeftRight,
110		q.keyMap.EnterSpace,
111	}
112}
113
114// FullHelp implements [help.KeyMap].
115func (q *Quit) FullHelp() [][]key.Binding {
116	return [][]key.Binding{
117		{q.keyMap.LeftRight, q.keyMap.EnterSpace, q.keyMap.Yes, q.keyMap.No},
118		{q.keyMap.Tab, q.keyMap.Close},
119	}
120}