Files
task-master/internal/ui/views/board.go
T
SkrueckenandCursor 41feba11de Add multiselect board filters and letter-jump on picker menus.
Persist comma-separated list filters per project, toggle with space, and jump by first letter in board and project pickers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 10:42:59 +02:00

122 lines
2.6 KiB
Go

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)
}