selector.go

  1package selector
  2
  3import (
  4	"github.com/charmbracelet/bubbles/key"
  5	"github.com/charmbracelet/bubbles/list"
  6	tea "github.com/charmbracelet/bubbletea"
  7	"github.com/charmbracelet/soft-serve/ui/common"
  8)
  9
 10// Selector is a list of items that can be selected.
 11type Selector struct {
 12	list   list.Model
 13	common common.Common
 14	active int
 15}
 16
 17// IdentifiableItem is an item that can be identified by a string and extends list.Item.
 18type IdentifiableItem interface {
 19	list.Item
 20	ID() string
 21}
 22
 23// SelectMsg is a message that is sent when an item is selected.
 24type SelectMsg string
 25
 26// ActiveMsg is a message that is sent when an item is active but not selected.
 27type ActiveMsg string
 28
 29// New creates a new selector.
 30func New(common common.Common, items []list.Item, delegate list.ItemDelegate) *Selector {
 31	l := list.New(items, delegate, common.Width, common.Height)
 32	l.SetShowTitle(false)
 33	l.SetShowHelp(false)
 34	l.SetShowStatusBar(false)
 35	l.DisableQuitKeybindings()
 36	s := &Selector{
 37		list:   l,
 38		common: common,
 39	}
 40	s.SetSize(common.Width, common.Height)
 41	return s
 42}
 43
 44// KeyMap returns the underlying list's keymap.
 45func (s *Selector) KeyMap() list.KeyMap {
 46	return s.list.KeyMap
 47}
 48
 49// SetSize implements common.Component.
 50func (s *Selector) SetSize(width, height int) {
 51	s.common.SetSize(width, height)
 52	s.list.SetSize(width, height)
 53}
 54
 55// SetItems sets the items in the selector.
 56func (s *Selector) SetItems(items []list.Item) tea.Cmd {
 57	return s.list.SetItems(items)
 58}
 59
 60// Index returns the index of the selected item.
 61func (s *Selector) Index() int {
 62	return s.list.Index()
 63}
 64
 65// Init implements tea.Model.
 66func (s *Selector) Init() tea.Cmd {
 67	return s.activeCmd
 68}
 69
 70// Update implements tea.Model.
 71func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 72	cmds := make([]tea.Cmd, 0)
 73	switch msg := msg.(type) {
 74	case tea.KeyMsg:
 75		switch {
 76		case key.Matches(msg, s.common.Keymap.Select):
 77			cmds = append(cmds, s.selectCmd)
 78		}
 79	}
 80	m, cmd := s.list.Update(msg)
 81	s.list = m
 82	if cmd != nil {
 83		cmds = append(cmds, cmd)
 84	}
 85	// Send ActiveMsg when index change.
 86	if s.active != s.list.Index() {
 87		cmds = append(cmds, s.activeCmd)
 88	}
 89	s.active = s.list.Index()
 90	return s, tea.Batch(cmds...)
 91}
 92
 93// View implements tea.Model.
 94func (s *Selector) View() string {
 95	return s.list.View()
 96}
 97
 98func (s *Selector) selectCmd() tea.Msg {
 99	item := s.list.SelectedItem()
100	i := item.(IdentifiableItem)
101	return SelectMsg(i.ID())
102}
103
104func (s *Selector) activeCmd() tea.Msg {
105	item := s.list.SelectedItem()
106	i := item.(IdentifiableItem)
107	return ActiveMsg(i.ID())
108}