quit.go

  1package dialog
  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// Quit represents a confirmation dialog for quitting the application.
 10type Quit struct {
 11	keyMap     QuitKeyMap
 12	selectedNo bool // true if "No" button is selected
 13}
 14
 15// NewQuit creates a new quit confirmation dialog.
 16func NewQuit() *Quit {
 17	q := &Quit{
 18		keyMap: DefaultQuitKeyMap(),
 19	}
 20	return q
 21}
 22
 23// ID implements [Model].
 24func (*Quit) ID() string {
 25	return "quit"
 26}
 27
 28// Update implements [Model].
 29func (q *Quit) Update(msg tea.Msg) (Model, tea.Cmd) {
 30	switch msg := msg.(type) {
 31	case tea.KeyPressMsg:
 32		switch {
 33		case key.Matches(msg, q.keyMap.LeftRight, q.keyMap.Tab):
 34			q.selectedNo = !q.selectedNo
 35			return q, nil
 36		case key.Matches(msg, q.keyMap.EnterSpace):
 37			if !q.selectedNo {
 38				return q, tea.Quit
 39			}
 40			return nil, nil
 41		case key.Matches(msg, q.keyMap.Yes):
 42			return q, tea.Quit
 43		case key.Matches(msg, q.keyMap.No, q.keyMap.Close):
 44			return nil, nil
 45		}
 46	}
 47
 48	return q, nil
 49}
 50
 51// View implements [Model].
 52func (q *Quit) View() string {
 53	const question = "Are you sure you want to quit?"
 54
 55	baseStyle := lipgloss.NewStyle()
 56	yesStyle := lipgloss.NewStyle()
 57	noStyle := yesStyle
 58
 59	if q.selectedNo {
 60		noStyle = noStyle.Foreground(lipgloss.Color("15")).Background(lipgloss.Color("15"))
 61		yesStyle = yesStyle.Background(lipgloss.Color("15"))
 62	} else {
 63		yesStyle = yesStyle.Foreground(lipgloss.Color("15")).Background(lipgloss.Color("15"))
 64		noStyle = noStyle.Background(lipgloss.Color("15"))
 65	}
 66
 67	const horizontalPadding = 3
 68	yesButton := yesStyle.PaddingLeft(horizontalPadding).Underline(true).Render("Y") +
 69		yesStyle.PaddingRight(horizontalPadding).Render("ep!")
 70	noButton := noStyle.PaddingLeft(horizontalPadding).Underline(true).Render("N") +
 71		noStyle.PaddingRight(horizontalPadding).Render("ope")
 72
 73	buttons := baseStyle.Width(lipgloss.Width(question)).Align(lipgloss.Right).Render(
 74		lipgloss.JoinHorizontal(lipgloss.Center, yesButton, "  ", noButton),
 75	)
 76
 77	content := baseStyle.Render(
 78		lipgloss.JoinVertical(
 79			lipgloss.Center,
 80			question,
 81			"",
 82			buttons,
 83		),
 84	)
 85
 86	quitDialogStyle := baseStyle.
 87		Padding(1, 2).
 88		Border(lipgloss.RoundedBorder()).
 89		BorderForeground(lipgloss.Color("15"))
 90
 91	return quitDialogStyle.Render(content)
 92}
 93
 94// ShortHelp implements [help.KeyMap].
 95func (q *Quit) ShortHelp() []key.Binding {
 96	return []key.Binding{
 97		q.keyMap.LeftRight,
 98		q.keyMap.EnterSpace,
 99	}
100}
101
102// FullHelp implements [help.KeyMap].
103func (q *Quit) FullHelp() [][]key.Binding {
104	return [][]key.Binding{
105		{q.keyMap.LeftRight, q.keyMap.EnterSpace, q.keyMap.Yes, q.keyMap.No},
106		{q.keyMap.Tab, q.keyMap.Close},
107	}
108}