bubble.go

  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}