on main help sign in
feat(ui): list: expose filterable items source type and return values for selection methods
commitc0be798cb5dfda1408a87f3a90bf8294aca6601a authorAyman Bagabas <ayman.bagabas@gmail.com> dateWed 17 Dec 2025, 16:36 (1 year ago) parent749a966
3 files changed+44−8
internal/ui/list/filterable.go+8−4 internal/ui/list/item.go+19 internal/ui/list/list.go+17−4
internal/ui/list/filterable.go+8−4
@@ -70,13 +70,17 @@ func (f *FilterableList) SetFilter(q string) {
7070 f.query = q
7171}
7272
73−type filterableItems []FilterableItem
73+// FilterableItemsSource is a type that implements [fuzzy.Source] for filtering
74+// [FilterableItem]s.
75+type FilterableItemsSource []FilterableItem
7476
75−func (f filterableItems) Len() int {
77+// Len returns the length of the source.
78+func (f FilterableItemsSource) Len() int {
7679 return len(f)
7780}
7881
79−func (f filterableItems) String(i int) string {
82+// String returns the string representation of the item at index i.
83+func (f FilterableItemsSource) String(i int) string {
8084 return f[i].Filter()
8185}
8286
@@ -94,7 +98,7 @@ func (f *FilterableList) VisibleItems() []Item {
9498 return items
9599 }
96100
97− items := filterableItems(f.items)
101+ items := FilterableItemsSource(f.items)
98102 matches := fuzzy.FindFrom(f.query, items)
99103 matchedItems := []Item{}
100104 resultSize := len(matches)
internal/ui/list/item.go+19
@@ -1,6 +1,8 @@
11package list
22
33import (
4+ "strings"
5+
46 "github.com/charmbracelet/x/ansi"
57)
68
@@ -30,3 +32,20 @@ type MouseClickable interface {
3032 // It returns true if the event was handled, false otherwise.
3133 HandleMouseClick(btn ansi.MouseButton, x, y int) bool
3234}
35+
36+// SpacerItem is a spacer item that adds vertical space in the list.
37+type SpacerItem struct {
38+ Height int
39+}
40+
41+// NewSpacerItem creates a new [SpacerItem] with the specified height.
42+func NewSpacerItem(height int) *SpacerItem {
43+ return &SpacerItem{
44+ Height: max(0, height-1),
45+ }
46+}
47+
48+// Render implements the Item interface for [SpacerItem].
49+func (s *SpacerItem) Render(width int) string {
50+ return strings.Repeat("\n", s.Height)
51+}
internal/ui/list/list.go+17−4
@@ -390,6 +390,7 @@ func (l *List) SelectedItemInView() bool {
390390}
391391
392392// SetSelected sets the selected item index in the list.
393+// It returns -1 if the index is out of bounds.
393394func (l *List) SetSelected(index int) {
394395 if index < 0 || index >= len(l.items) {
395396 l.selectedIdx = -1
@@ -415,31 +416,43 @@ func (l *List) IsSelectedLast() bool {
415416}
416417
417418// SelectPrev selects the previous item in the list.
418−func (l *List) SelectPrev() {
419+// It returns whether the selection changed.
420+func (l *List) SelectPrev() bool {
419421 if l.selectedIdx > 0 {
420422 l.selectedIdx--
423+ return true
421424 }
425+ return false
422426}
423427
424428// SelectNext selects the next item in the list.
425−func (l *List) SelectNext() {
429+// It returns whether the selection changed.
430+func (l *List) SelectNext() bool {
426431 if l.selectedIdx < len(l.items)-1 {
427432 l.selectedIdx++
433+ return true
428434 }
435+ return false
429436}
430437
431438// SelectFirst selects the first item in the list.
432−func (l *List) SelectFirst() {
439+// It returns whether the selection changed.
440+func (l *List) SelectFirst() bool {
433441 if len(l.items) > 0 {
434442 l.selectedIdx = 0
443+ return true
435444 }
445+ return false
436446}
437447
438448// SelectLast selects the last item in the list.
439−func (l *List) SelectLast() {
449+// It returns whether the selection changed.
450+func (l *List) SelectLast() bool {
440451 if len(l.items) > 0 {
441452 l.selectedIdx = len(l.items) - 1
453+ return true
442454 }
455+ return false
443456}
444457
445458// SelectedItem returns the currently selected item. It may be nil if no item