1package selection
2
3import (
4 "strings"
5
6 tea "github.com/charmbracelet/bubbletea"
7 "github.com/charmbracelet/lipgloss"
8 "github.com/charmbracelet/soft/tui/style"
9 "github.com/muesli/reflow/truncate"
10)
11
12type SelectedMsg struct {
13 Name string
14 Index int
15}
16
17type ActiveMsg struct {
18 Name string
19 Index int
20}
21
22type Bubble struct {
23 Items []string
24 SelectedItem int
25 styles *style.Styles
26}
27
28func NewBubble(items []string, styles *style.Styles) *Bubble {
29 return &Bubble{
30 Items: items,
31 styles: styles,
32 }
33}
34
35func (b *Bubble) Init() tea.Cmd {
36 return nil
37}
38
39func (b Bubble) View() string {
40 s := strings.Builder{}
41 repoNameMaxWidth := b.styles.Menu.GetWidth() - // menu width
42 b.styles.Menu.GetHorizontalPadding() - // menu padding
43 lipgloss.Width(b.styles.MenuCursor.String()) - // cursor
44 b.styles.MenuItem.GetHorizontalFrameSize() // menu item gaps
45 for i, item := range b.Items {
46 item := truncate.StringWithTail(item, uint(repoNameMaxWidth), "…")
47 if i == b.SelectedItem {
48 s.WriteString(b.styles.MenuCursor.String())
49 s.WriteString(b.styles.SelectedMenuItem.Render(item))
50 } else {
51 s.WriteString(b.styles.MenuItem.Render(item))
52 }
53 if i < len(b.Items)-1 {
54 s.WriteRune('\n')
55 }
56 }
57 return s.String()
58}
59
60func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
61 cmds := make([]tea.Cmd, 0)
62 switch msg := msg.(type) {
63 case tea.KeyMsg:
64 switch msg.String() {
65 case "k", "up":
66 if b.SelectedItem > 0 {
67 b.SelectedItem--
68 cmds = append(cmds, b.sendActiveMessage)
69 }
70 case "j", "down":
71 if b.SelectedItem < len(b.Items)-1 {
72 b.SelectedItem++
73 cmds = append(cmds, b.sendActiveMessage)
74 }
75 case "enter":
76 cmds = append(cmds, b.sendSelectedMessage)
77 }
78 }
79 return b, tea.Batch(cmds...)
80}
81
82func (b *Bubble) sendActiveMessage() tea.Msg {
83 if b.SelectedItem >= 0 && b.SelectedItem < len(b.Items) {
84 return ActiveMsg{
85 Name: b.Items[b.SelectedItem],
86 Index: b.SelectedItem,
87 }
88 }
89 return nil
90}
91
92func (b *Bubble) sendSelectedMessage() tea.Msg {
93 if b.SelectedItem >= 0 && b.SelectedItem < len(b.Items) {
94 return SelectedMsg{
95 Name: b.Items[b.SelectedItem],
96 Index: b.SelectedItem,
97 }
98 }
99 return nil
100}