Files
terminalShop-old/pkg/tui/views.go
marcus06 52d533703b feat(tui): remake parts of the tui (#46)
* feat(tui): remake parts of the tui

* chore(tui): remove unused newViewport helper
2026-06-11 11:24:56 +02:00

159 lines
4.3 KiB
Go

package tui
import (
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
)
// ResizeView renders a message when the terminal is too small.
func (m Model) ResizeView() string {
msg := m.theme.TextDim().
Align(lipgloss.Center).
Render("Terminal too small.\nPlease resize.")
return lipgloss.Place(
m.viewportWidth,
m.viewportHeight,
lipgloss.Center,
lipgloss.Center,
msg,
)
}
func (m Model) View() tea.View {
v := tea.NewView(m.renderString())
v.AltScreen = true
return v
}
func (m Model) renderString() string {
if m.viewportWidth == 0 {
m.updateLayout(120, 30)
}
// If the terminal is too small, show a resize message
if m.size == undersized {
return m.ResizeView()
}
if m.currentPage == splashPage {
return m.SplashView()
}
// If the menu modal is showing, render it full-screen (bypass container)
if m.ShowingMenu {
return m.BuildMenuView()
}
// If the help modal is showing, render it full-screen (bypass container)
if m.ShowingHelp {
return m.BuildHelpView()
}
// Build header with cart tab
header := m.BuildHeader()
// Build breadcrumbs
breadcrumbs := m.BuildBreadcrumbs()
// Calculate available content height within the container
headerHeight := lipgloss.Height(header)
breadcrumbsHeight := 0
if breadcrumbs != "" {
breadcrumbsHeight = lipgloss.Height(breadcrumbs)
}
footer := m.BuildFooter()
footerHeight := lipgloss.Height(footer)
marginTop := 1
marginBottom := 1
bufferSpace := 1
reservedHeight := headerHeight + breadcrumbsHeight + footerHeight + marginTop + marginBottom + bufferSpace
availableContentHeight := m.heightContainer - reservedHeight
// Ensure minimum content height
if availableContentHeight < 3 {
availableContentHeight = 3
marginTop = 0
marginBottom = 0
}
var content string
if m.Loading {
loadingStyle := m.theme.TextLoading().Padding(2, 4)
content = loadingStyle.Render("Loading products from API...")
} else if m.error != nil {
errorStyle := m.theme.PanelError().Padding(0, 1).MarginBottom(1)
// soft-wrap long messages instead of clipping.
if m.widthContent > 4 {
errorStyle = errorStyle.Width(m.widthContent - 4)
}
errorBanner := errorStyle.Render(m.error.message)
content = errorBanner + "\n" + m.buildPageContent()
} else if m.notice != nil {
noticeStyle := m.theme.TextSuccess().Border(lipgloss.NormalBorder()).BorderForeground(m.theme.Success()).Padding(0, 1).MarginBottom(1)
noticeBanner := noticeStyle.Render(m.notice.message)
content = noticeBanner + "\n" + m.buildPageContent()
} else {
content = m.buildPageContent()
}
isViewportPage := m.currentPage == shopPage || m.currentPage == cartPage ||
m.currentPage == shippingPage || m.currentPage == paymentPage ||
m.currentPage == confirmPage || m.currentPage == accountPage
var child string
if isViewportPage {
if breadcrumbs != "" {
child = lipgloss.JoinVertical(lipgloss.Left, header, breadcrumbs, content, footer)
} else {
child = lipgloss.JoinVertical(lipgloss.Left, header, content, footer)
}
} else if breadcrumbs != "" {
contentWithPadding := lipgloss.NewStyle().MarginTop(marginTop).MarginBottom(marginBottom).Height(availableContentHeight).Render(content)
child = lipgloss.JoinVertical(lipgloss.Left, header, breadcrumbs, contentWithPadding, footer)
} else {
contentWithPadding := lipgloss.NewStyle().MarginTop(marginTop).MarginBottom(marginBottom).Height(availableContentHeight).Render(content)
child = lipgloss.JoinVertical(lipgloss.Left, header, contentWithPadding, footer)
}
// Constrain the assembled layout to the container dimensions
constrained := lipgloss.NewStyle().
MaxWidth(m.widthContainer).
MaxHeight(m.heightContainer).
Render(child)
if m.refund.open {
constrained = m.RenderRefundOverlay()
}
// Center the entire container in the terminal viewport
return lipgloss.Place(
m.viewportWidth,
m.viewportHeight,
lipgloss.Center,
lipgloss.Center,
constrained,
)
}
func (m Model) buildPageContent() string {
switch m.currentPage {
case accountPage:
return m.BuildAccountView()
case shippingPage:
return m.ShippingPageView()
case paymentPage:
return m.PaymentPageView()
case reviewPage:
return m.ReviewView()
case confirmPage:
return m.ConfirmView()
case cartPage:
return m.CartView()
case shopPage:
return m.ShopView()
default:
return m.ShopView()
}
}