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}