Skip to content

Commit f247528

Browse files
authored
fix: paginate selector (#37)
2 parents 7d10770 + e0e1590 commit f247528

File tree

1 file changed

+72
-15
lines changed

1 file changed

+72
-15
lines changed

pkg/ui/selector.go

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@ import (
55
"strings"
66

77
tea "github.com/charmbracelet/bubbletea"
8+
"github.com/charmbracelet/lipgloss"
89
)
910

1011
const AllNonManagementText = "all non management accounts"
1112

13+
var (
14+
gray = lipgloss.Color("240")
15+
pink = lipgloss.Color("205")
16+
17+
helpStyle = lipgloss.NewStyle().Foreground(gray)
18+
filterPlaceholderStyle = lipgloss.NewStyle().Foreground(gray).Faint(true)
19+
cursorStyle = lipgloss.NewStyle().Foreground(pink)
20+
21+
helpTextMultipleChoice = "↑/↓/←/→: Navigate • Space: Select • Enter: Confirm"
22+
)
23+
1224
type model struct {
1325
question string
1426
choices []string
@@ -18,6 +30,8 @@ type model struct {
1830
selected map[string]struct{}
1931
allChoicesMap map[int]string // Maps original index to choice text
2032
quit bool
33+
pageSize int
34+
currentPage int
2135
}
2236

2337
func (m model) Init() tea.Cmd {
@@ -31,25 +45,43 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
3145
case "ctrl+c":
3246
m.quit = true
3347
return m, tea.Quit
48+
case "enter":
49+
if len(m.selected) > 0 {
50+
return m, tea.Quit
51+
}
3452
case "up":
3553
if m.cursor > 0 {
3654
m.cursor--
55+
} else if m.currentPage > 0 {
56+
m.currentPage--
57+
m.cursor = m.pageSize - 1
3758
}
3859
case "down":
3960
if m.cursor < len(m.filtered)-1 {
4061
m.cursor++
62+
} else if (m.currentPage+1)*m.pageSize < len(m.filtered) {
63+
m.currentPage++
64+
m.cursor = 0
65+
}
66+
case "left":
67+
if m.currentPage > 0 {
68+
m.currentPage--
69+
m.cursor = 0
70+
}
71+
case "right":
72+
if (m.currentPage+1)*m.pageSize < len(m.filtered) {
73+
m.currentPage++
74+
m.cursor = 0
4175
}
4276
case " ":
4377
if len(m.filtered) > 0 {
44-
currentChoice := m.filtered[m.cursor]
78+
currentChoice := m.filtered[m.currentPage*m.pageSize+m.cursor]
4579
if _, ok := m.selected[currentChoice]; ok {
4680
delete(m.selected, currentChoice)
4781
} else {
4882
m.selected[currentChoice] = struct{}{}
4983
}
5084
}
51-
case "enter":
52-
return m, tea.Quit
5385
case "backspace":
5486
if len(m.filter) > 0 {
5587
m.filter = m.filter[:len(m.filter)-1]
@@ -77,36 +109,59 @@ func (m *model) updateFilteredChoices() {
77109
}
78110
}
79111
}
80-
if len(m.filtered) == 0 {
81-
m.cursor = 0
82-
} else if m.cursor >= len(m.filtered) {
83-
m.cursor = len(m.filtered) - 1
84-
}
112+
113+
// Reset pagination when filter changes
114+
m.currentPage = 0
115+
m.cursor = 0
85116
}
86117

87118
func (m model) View() string {
88-
s := fmt.Sprintf("%s: [Use arrows to move, space to select, enter to confirm, type to filter]\n\n", m.question)
89-
s += fmt.Sprintf("Filter: %s\n\n", m.filter)
119+
s := fmt.Sprintf("%s:\n\n", m.question)
120+
121+
filterText := "Filter: "
122+
if m.filter == "" {
123+
filterText += filterPlaceholderStyle.Render("type to filter")
124+
} else {
125+
filterText += m.filter
126+
}
127+
s += fmt.Sprintf("%s\n\n", helpStyle.Render(filterText))
90128

91129
if len(m.filtered) == 0 {
92130
s += "No matches found.\n"
93131
} else {
94-
for i, choice := range m.filtered {
132+
start := m.currentPage * m.pageSize
133+
end := min(start+m.pageSize, len(m.filtered))
134+
135+
for i, choice := range m.filtered[start:end] {
95136
cursor := " "
96137
if m.cursor == i {
97-
cursor = ">"
138+
cursor = cursorStyle.Render(">")
98139
}
99140

100141
checked := " "
101142
if _, ok := m.selected[choice]; ok {
102-
checked = "x"
143+
checked = cursorStyle.Render("x")
103144
}
104145

105146
s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
106147
}
148+
149+
if len(m.filtered) > m.pageSize {
150+
s += fmt.Sprintf("\n%s\n",
151+
helpStyle.Render(fmt.Sprintf("Page %d/%d (Showing %d-%d of %d items)",
152+
m.currentPage+1,
153+
(len(m.filtered)-1)/m.pageSize+1,
154+
start+1,
155+
end,
156+
len(m.filtered))))
157+
}
158+
}
159+
160+
if len(m.selected) == 0 {
161+
s += "\n" + helpStyle.Render("Please select at least one item")
107162
}
108163

109-
s += "\n"
164+
s += "\n" + helpStyle.Render(helpTextMultipleChoice)
110165

111166
return s
112167
}
@@ -126,7 +181,9 @@ func Prompt(question string, choices []string) ([]int, error) {
126181
}
127182
return m
128183
}(),
129-
quit: false,
184+
quit: false,
185+
pageSize: 10,
186+
currentPage: 0,
130187
}
131188

132189
p := tea.NewProgram(m)

0 commit comments

Comments
 (0)