package views import ( "fmt" "strings" "unicode" "task/internal/ticket" ) // BoardMenu renders a multi-select board-list picker overlay. type BoardMenu struct { Cursor int Width int Styles StatusMenuStyles selected map[ticket.BoardList]bool } // NewBoardMenu creates a menu with the given filter pre-selected. func NewBoardMenu(current ticket.BoardFilter, width int, styles StatusMenuStyles) BoardMenu { current = ticket.NormalizeFilter(current) sel := map[ticket.BoardList]bool{} for _, bl := range current { sel[bl] = true } cursor := 0 for i, bl := range ticket.AllBoardLists { if sel[bl] { cursor = i break } } return BoardMenu{ Cursor: cursor, Width: width, Styles: styles, selected: sel, } } // MoveDown moves the selection down. func (m *BoardMenu) MoveDown() { if m.Cursor < len(ticket.AllBoardLists)-1 { m.Cursor++ } } // MoveUp moves the selection up. func (m *BoardMenu) MoveUp() { if m.Cursor > 0 { m.Cursor-- } } // JumpToShortcut moves the cursor to the list whose label starts with r. func (m *BoardMenu) JumpToShortcut(r rune) bool { r = unicode.ToLower(r) for i, bl := range ticket.AllBoardLists { if bl.Shortcut() == r { m.Cursor = i return true } } return false } // Toggle flips the checked state of the highlighted list. func (m *BoardMenu) Toggle() { if m.selected == nil { m.selected = map[ticket.BoardList]bool{} } bl := ticket.AllBoardLists[m.Cursor] if bl == ticket.BoardAll { m.selected = map[ticket.BoardList]bool{ticket.BoardAll: true} return } delete(m.selected, ticket.BoardAll) if m.selected[bl] { delete(m.selected, bl) } else { m.selected[bl] = true } if len(m.selected) == 0 { m.selected[ticket.BoardAll] = true } } // Selected returns the current multi-select filter. func (m BoardMenu) Selected() ticket.BoardFilter { var out ticket.BoardFilter for _, bl := range ticket.AllBoardLists { if m.selected[bl] { out = append(out, bl) } } return ticket.NormalizeFilter(out) } // View renders the board menu. func (m BoardMenu) View() string { var lines []string lines = append(lines, m.Styles.Title.Render("Filter lists")) for i, bl := range ticket.AllBoardLists { mark := "[ ]" if m.selected[bl] { mark = "[x]" } entry := fmt.Sprintf("%s %c %s", mark, bl.Shortcut(), bl.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(44, w-2)).Render(body) }