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	TabDot       lipgloss.Style
 26	UseDot       bool
 27}
 28
 29// New creates a new Tabs component.
 30func New(c common.Common, tabs []string) *Tabs {
 31	r := &Tabs{
 32		common:       c,
 33		tabs:         tabs,
 34		activeTab:    0,
 35		TabSeparator: c.Styles.TabSeparator,
 36		TabInactive:  c.Styles.TabInactive,
 37		TabActive:    c.Styles.TabActive,
 38	}
 39	return r
 40}
 41
 42// SetSize implements common.Component.
 43func (t *Tabs) SetSize(width, height int) {
 44	t.common.SetSize(width, height)
 45}
 46
 47// Init implements tea.Model.
 48func (t *Tabs) Init() tea.Cmd {
 49	t.activeTab = 0
 50	return nil
 51}
 52
 53// Update implements tea.Model.
 54func (t *Tabs) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 55	cmds := make([]tea.Cmd, 0)
 56	switch msg := msg.(type) {
 57	case tea.KeyMsg:
 58		switch msg.String() {
 59		case "tab":
 60			t.activeTab = (t.activeTab + 1) % len(t.tabs)
 61			cmds = append(cmds, t.activeTabCmd)
 62		case "shift+tab":
 63			t.activeTab = (t.activeTab - 1 + len(t.tabs)) % len(t.tabs)
 64			cmds = append(cmds, t.activeTabCmd)
 65		}
 66	case SelectTabMsg:
 67		tab := int(msg)
 68		if tab >= 0 && tab < len(t.tabs) {
 69			t.activeTab = int(msg)
 70		}
 71	}
 72	return t, tea.Batch(cmds...)
 73}
 74
 75// View implements tea.Model.
 76func (t *Tabs) View() string {
 77	s := strings.Builder{}
 78	sep := t.TabSeparator
 79	for i, tab := range t.tabs {
 80		style := t.TabInactive.Copy()
 81		prefix := "  "
 82		if i == t.activeTab {
 83			style = t.TabActive.Copy()
 84			prefix = t.TabDot.Render("• ")
 85		}
 86		if t.UseDot {
 87			s.WriteString(prefix)
 88		}
 89		s.WriteString(style.Render(tab))
 90		if i != len(t.tabs)-1 {
 91			s.WriteString(sep.String())
 92		}
 93	}
 94	return lipgloss.NewStyle().
 95		MaxWidth(t.common.Width).
 96		Render(s.String())
 97}
 98
 99func (t *Tabs) activeTabCmd() tea.Msg {
100	return ActiveTabMsg(t.activeTab)
101}
102
103// SelectTabCmd is a bubbletea command that selects the tab at the given index.
104func SelectTabCmd(tab int) tea.Cmd {
105	return func() tea.Msg {
106		return SelectTabMsg(tab)
107	}
108}