package views import ( "fmt" "strings" "unicode" "github.com/charmbracelet/lipgloss" "task/internal/ticket" ) // StatusMenu renders a status picker overlay. type StatusMenu struct { Cursor int Width int Styles StatusMenuStyles } // StatusMenuStyles holds styles for the status picker. type StatusMenuStyles struct { Box lipgloss.Style Title lipgloss.Style Selected lipgloss.Style Normal lipgloss.Style } // NewStatusMenu creates a menu with the given status pre-selected. func NewStatusMenu(current ticket.Status, width int, styles StatusMenuStyles) StatusMenu { return StatusMenu{ Cursor: current.Index(), Width: width, Styles: styles, } } // MoveDown moves the selection down. func (m *StatusMenu) MoveDown() { if m.Cursor < len(ticket.AllStatuses)-1 { m.Cursor++ } } // MoveUp moves the selection up. func (m *StatusMenu) MoveUp() { if m.Cursor > 0 { m.Cursor-- } } // JumpToShortcut moves the cursor to the status whose label starts with r. func (m *StatusMenu) JumpToShortcut(r rune) bool { r = unicode.ToLower(r) for i, st := range ticket.AllStatuses { if st.Shortcut() == r { m.Cursor = i return true } } return false } // Selected returns the currently highlighted status. func (m StatusMenu) Selected() ticket.Status { if m.Cursor < 0 || m.Cursor >= len(ticket.AllStatuses) { return ticket.StatusTodo } return ticket.AllStatuses[m.Cursor] } // View renders the status menu. func (m StatusMenu) View() string { var lines []string lines = append(lines, m.Styles.Title.Render("Set status")) for i, st := range ticket.AllStatuses { entry := fmt.Sprintf("%c %s", st.Shortcut(), st.Label()) if i == m.Cursor { lines = append(lines, m.Styles.Selected.Render("▸ "+entry)) } else { lines = append(lines, m.Styles.Normal.Render(" "+entry)) } } body := strings.Join(lines, "\n") w := m.Width if w < 20 { w = 20 } return m.Styles.Box.Width(min(40, w-2)).Render(body) } func min(a, b int) int { if a < b { return a } return b }