tabs.go

 1package tabs
 2
 3import (
 4	"strings"
 5
 6	tea "github.com/charmbracelet/bubbletea"
 7	"github.com/charmbracelet/lipgloss"
 8	"github.com/charmbracelet/soft-serve/ui/common"
 9)
10
11// SelectTabMsg is a message that contains the index of the tab to select.
12type SelectTabMsg int
13
14// ActiveTabMsg is a message that contains the index of the current active tab.
15type ActiveTabMsg int
16
17// Tabs is bubbletea component that displays a list of tabs.
18type Tabs struct {
19	common       common.Common
20	tabs         []string
21	activeTab    int
22	TabSeparator lipgloss.Style
23	TabInactive  lipgloss.Style
24	TabActive    lipgloss.Style
25}
26
27// New creates a new Tabs component.
28func New(c common.Common, tabs []string) *Tabs {
29	r := &Tabs{
30		common:       c,
31		tabs:         tabs,
32		activeTab:    0,
33		TabSeparator: c.Styles.TabSeparator,
34		TabInactive:  c.Styles.TabInactive,
35		TabActive:    c.Styles.TabActive,
36	}
37	return r
38}
39
40// SetSize implements common.Component.
41func (t *Tabs) SetSize(width, height int) {
42	t.common.SetSize(width, height)
43}
44
45// Init implements tea.Model.
46func (t *Tabs) Init() tea.Cmd {
47	t.activeTab = 0
48	return nil
49}
50
51// Update implements tea.Model.
52func (t *Tabs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
53	cmds := make([]tea.Cmd, 0)
54	switch msg := msg.(type) {
55	case tea.KeyMsg:
56		switch msg.String() {
57		case "tab":
58			t.activeTab = (t.activeTab + 1) % len(t.tabs)
59			cmds = append(cmds, t.activeTabCmd)
60		case "shift+tab":
61			t.activeTab = (t.activeTab - 1 + len(t.tabs)) % len(t.tabs)
62			cmds = append(cmds, t.activeTabCmd)
63		}
64	case SelectTabMsg:
65		tab := int(msg)
66		if tab >= 0 && tab < len(t.tabs) {
67			t.activeTab = int(msg)
68		}
69	}
70	return t, tea.Batch(cmds...)
71}
72
73// View implements tea.Model.
74func (t *Tabs) View() string {
75	s := strings.Builder{}
76	sep := t.TabSeparator
77	for i, tab := range t.tabs {
78		style := t.TabInactive.Copy()
79		if i == t.activeTab {
80			style = t.TabActive.Copy()
81		}
82		s.WriteString(style.Render(tab))
83		if i != len(t.tabs)-1 {
84			s.WriteString(sep.String())
85		}
86	}
87	return s.String()
88}
89
90func (t *Tabs) activeTabCmd() tea.Msg {
91	return ActiveTabMsg(t.activeTab)
92}
93
94// SelectTabCmd is a bubbletea command that selects the tab at the given index.
95func SelectTabCmd(tab int) tea.Cmd {
96	return func() tea.Msg {
97		return SelectTabMsg(tab)
98	}
99}