tabs.go RawHistoryFolder
1package styles
2
3import (
4 "fmt"
5
6 "github.com/charmbracelet/lipgloss"
7)
8
9// TabsModel represents a horizontal tab bar.
10type TabsModel struct {
11 Tabs []string
12 Active int
13 theme *Theme
14}
15
16// NewTabs creates a new tab bar with the given labels.
17func NewTabs(theme *Theme, tabs ...string) TabsModel {
18 return TabsModel{
19 Tabs: tabs,
20 Active: 0,
21 theme: theme,
22 }
23}
24
25// SetActive sets the active tab index.
26func (m *TabsModel) SetActive(index int) {
27 if index >= 0 && index < len(m.Tabs) {
28 m.Active = index
29 }
30}
31
32// Next moves to the next tab.
33func (m *TabsModel) Next() {
34 m.Active = (m.Active + 1) % len(m.Tabs)
35}
36
37// Prev moves to the previous tab.
38func (m *TabsModel) Prev() {
39 m.Active = (m.Active - 1 + len(m.Tabs)) % len(m.Tabs)
40}
41
42// View renders the tab bar.
43func (m TabsModel) View() string {
44 var tabs []string
45
46 for i, tab := range m.Tabs {
47 style := m.theme.InactiveTab
48 if i == m.Active {
49 style = m.theme.ActiveTab
50 }
51 tabs = append(tabs, style.Render(tab))
52 }
53
54 // Join tabs with separator
55 gap := lipgloss.NewStyle().
56 Foreground(m.theme.Border).
57 Render(" │ ")
58
59 row := ""
60 for i, tab := range tabs {
61 if i > 0 {
62 row += gap
63 }
64 row += tab
65 }
66
67 const tabBarWidth = 80
68 return m.theme.TabBar.Width(tabBarWidth).Render(row)
69}
70
71// ViewCompact renders a compact tab bar without separators.
72func (m TabsModel) ViewCompact() string {
73 var tabs []string
74
75 for i, tab := range m.Tabs {
76 style := m.theme.InactiveTab
77 if i == m.Active {
78 style = m.theme.ActiveTab
79 }
80 tabs = append(tabs, style.Render(tab))
81 }
82
83 return lipgloss.JoinHorizontal(lipgloss.Top, tabs...)
84}
85
86// TimelineTabs creates tabs for history timeline view.
87func TimelineTabs(theme *Theme) TabsModel {
88 return NewTabs(theme, "Today", "Yesterday", "This Week", "Older")
89}
90
91// ViewWithCounts renders tabs with item counts.
92func (m TabsModel) ViewWithCounts(counts []int) string {
93 var tabs []string
94
95 for i, tab := range m.Tabs {
96 count := 0
97 if i < len(counts) {
98 count = counts[i]
99 }
100
101 label := tab
102 if count > 0 {
103 label = lipgloss.JoinHorizontal(
104 lipgloss.Center,
105 tab,
106 " ",
107 m.theme.BadgeMuted.Render(formatCount(count)),
108 )
109 }
110
111 style := m.theme.InactiveTab
112 if i == m.Active {
113 style = m.theme.ActiveTab
114 }
115 tabs = append(tabs, style.Render(label))
116 }
117
118 gap := lipgloss.NewStyle().
119 Foreground(m.theme.Border).
120 Render(" ")
121
122 return lipgloss.JoinHorizontal(lipgloss.Top, join(tabs, gap)...)
123}
124
125// join inserts a separator between items.
126func join(items []string, sep string) []string {
127 if len(items) == 0 {
128 return items
129 }
130 result := make([]string, 0, len(items)*2-1)
131 for i, item := range items {
132 if i > 0 {
133 result = append(result, sep)
134 }
135 result = append(result, item)
136 }
137 return result
138}
139
140// formatCount formats a count for display.
141func formatCount(n int) string {
142 if n >= 1000 {
143 return "999+"
144 }
145 return fmt.Sprintf("%d", n)
146}