diff --git a/examples/tabs/README.md b/examples/tabs/README.md deleted file mode 100644 index 20b88b576a..0000000000 --- a/examples/tabs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# tabs Example - -![tabs Recording](recording-tabs.gif) \ No newline at end of file diff --git a/examples/tabs/main.go b/examples/tabs/main.go index ec2d2ec04b..f5cce63e56 100644 --- a/examples/tabs/main.go +++ b/examples/tabs/main.go @@ -12,8 +12,7 @@ import ( type model struct { Tabs []string TabContent []string - - activatedTab int + activeTab int } func (m model) Init() tea.Cmd { @@ -24,20 +23,13 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - // These keys should exit the program. case "ctrl+c", "q": return m, tea.Quit - // Cycle through tabs to the right - case "right": - if m.activatedTab < len(m.Tabs)-1 { - m.activatedTab++ - } + case "right", "l", "n", "tab": + m.activeTab = min(m.activeTab+1, len(m.Tabs)-1) return m, nil - // Cycle through tabs to the left - case "left": - if m.activatedTab > 0 { - m.activatedTab-- - } + case "left", "h", "p", "shift+tab": + m.activeTab = max(m.activeTab-1, 0) return m, nil } } @@ -45,101 +37,80 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } +func tabBorderWithBottom(left, middle, right string) lipgloss.Border { + border := lipgloss.RoundedBorder() + border.BottomLeft = left + border.Bottom = middle + border.BottomRight = right + return border +} + var ( defaultWidth = 20 - activeTabBorder = lipgloss.Border{ - Top: "─", - Bottom: " ", - Left: "│", - Right: "│", - TopLeft: "╭", - TopRight: "╮", - BottomLeft: "┘", - BottomRight: "└", - } - highlight = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} - tabBorder = lipgloss.Border{ - Top: "─", - Bottom: "─", - Left: "│", - Right: "│", - TopLeft: "╭", - TopRight: "╮", - BottomLeft: "┴", - BottomRight: "┴", - } - tab = lipgloss.NewStyle(). - Border(tabBorder, true). - BorderForeground(highlight). - Padding(0, 1) - activeTab = tab.Copy().Border(activeTabBorder, true) - - tabGap = tab.Copy(). - BorderTop(false). - BorderLeft(false). - BorderRight(false) - - docStyle = lipgloss.NewStyle().Padding(1, 2, 1, 2) + inactiveTabBorder = tabBorderWithBottom("┴", "─", "┴") + activeTabBorder = tabBorderWithBottom("┘", " ", "└") + docStyle = lipgloss.NewStyle().Padding(1, 2, 1, 2) + highlightColor = lipgloss.AdaptiveColor{Light: "#874BFD", Dark: "#7D56F4"} + inactiveTabStyle = lipgloss.NewStyle().Border(inactiveTabBorder, true).BorderForeground(highlightColor).Padding(0, 1) + activeTabStyle = inactiveTabStyle.Copy().Border(activeTabBorder, true) + windowStyle = lipgloss.NewStyle().BorderForeground(highlightColor).Padding(2, 0).Align(lipgloss.Center).Border(lipgloss.NormalBorder()).UnsetBorderTop() ) -func max(a, b int) int { - if a > b { - return a - } - return b -} - func (m model) View() string { doc := strings.Builder{} - // Tabs - { - var renderedTabs []string - - // Activate the correct tab - for i, t := range m.Tabs { - if i == m.activatedTab { - renderedTabs = append(renderedTabs, activeTab.Render(t)) - } else { - renderedTabs = append(renderedTabs, tab.Render(t)) - } - } + var renderedTabs []string - row := lipgloss.JoinHorizontal( - lipgloss.Top, - renderedTabs..., - ) - gap := tabGap.Render(strings.Repeat(" ", max(0, defaultWidth-lipgloss.Width(row)-2))) - row = lipgloss.JoinHorizontal(lipgloss.Bottom, row, gap) - doc.WriteString(row + "\n\n") + for i, t := range m.Tabs { + var style lipgloss.Style + isFirst, isLast, isActive := i == 0, i == len(m.Tabs)-1, i == m.activeTab + if isActive { + style = activeTabStyle.Copy() + } else { + style = inactiveTabStyle.Copy() + } + border, _, _, _, _ := style.GetBorder() + if isFirst && isActive { + border.BottomLeft = "│" + } else if isFirst && !isActive { + border.BottomLeft = "├" + } else if isLast && isActive { + border.BottomRight = "│" + } else if isLast && !isActive { + border.BottomRight = "┤" + } + style = style.Border(border) + renderedTabs = append(renderedTabs, style.Render(t)) } - doc.WriteString(m.TabContent[m.activatedTab]) - + row := lipgloss.JoinHorizontal(lipgloss.Top, renderedTabs...) + doc.WriteString(row) + doc.WriteString("\n") + doc.WriteString(windowStyle.Width((lipgloss.Width(row) - windowStyle.GetHorizontalFrameSize())).Render(m.TabContent[m.activeTab])) return docStyle.Render(doc.String()) } func main() { - tabs := []string{ - "Lip Gloss", - "Blush", - "Eye Shadow", - "Mascara", - "Foundation", - } - - tabContent := []string{ - "tab1", - "tab2", - "tab3", - "tab4", - "tab5", - } - + tabs := []string{"Lip Gloss", "Blush", "Eye Shadow", "Mascara", "Foundation"} + tabContent := []string{"Lip Gloss Tab", "Blush Tab", "Eye Shadow Tab", "Mascara Tab", "Foundation Tab"} m := model{Tabs: tabs, TabContent: tabContent} if err := tea.NewProgram(m).Start(); err != nil { fmt.Println("Error running program:", err) os.Exit(1) } } + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/examples/tabs/recording-tabs.gif b/examples/tabs/recording-tabs.gif deleted file mode 100644 index ac057ae1ea..0000000000 Binary files a/examples/tabs/recording-tabs.gif and /dev/null differ