feat(list): ability to SetStatusBarItemName (#169)

This commit is contained in:
Weslei Juan Novaes Pereira 2022-06-16 19:14:47 -03:00 committed by GitHub
parent e57fd292cc
commit 57d79daf4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 7 deletions

View File

@ -87,7 +87,7 @@ type Rank struct {
// DefaultFilter uses the sahilm/fuzzy to filter through the list. // DefaultFilter uses the sahilm/fuzzy to filter through the list.
// This is set by default. // This is set by default.
func DefaultFilter(term string, targets []string) []Rank { func DefaultFilter(term string, targets []string) []Rank {
var ranks fuzzy.Matches = fuzzy.Find(term, targets) var ranks = fuzzy.Find(term, targets)
sort.Stable(ranks) sort.Stable(ranks)
result := make([]Rank, len(ranks)) result := make([]Rank, len(ranks))
for i, r := range ranks { for i, r := range ranks {
@ -129,6 +129,9 @@ type Model struct {
showHelp bool showHelp bool
filteringEnabled bool filteringEnabled bool
itemNameSingular string
itemNamePlural string
Title string Title string
Styles Styles Styles Styles
@ -202,6 +205,8 @@ func New(items []Item, delegate ItemDelegate, width, height int) Model {
showStatusBar: true, showStatusBar: true,
showPagination: true, showPagination: true,
showHelp: true, showHelp: true,
itemNameSingular: "item",
itemNamePlural: "items",
filteringEnabled: true, filteringEnabled: true,
KeyMap: DefaultKeyMap(), KeyMap: DefaultKeyMap(),
Filter: DefaultFilter, Filter: DefaultFilter,
@ -286,6 +291,18 @@ func (m Model) ShowStatusBar() bool {
return m.showStatusBar return m.showStatusBar
} }
// SetStatusBarItemName defines a replacement for the items identifier. Defaults
// to item/items
func (m *Model) SetStatusBarItemName(singular, plural string) {
m.itemNameSingular = singular
m.itemNamePlural = plural
}
// StatusBarItemName returns singular and plural status bar item names
func (m Model) StatusBarItemName() (string, string) {
return m.itemNameSingular, m.itemNamePlural
}
// ShowingPagination hides or shoes the paginator. Note that pagination will // ShowingPagination hides or shoes the paginator. Note that pagination will
// still be active, it simply won't be displayed. // still be active, it simply won't be displayed.
func (m *Model) SetShowPagination(v bool) { func (m *Model) SetShowPagination(v bool) {
@ -1048,21 +1065,25 @@ func (m Model) statusView() string {
totalItems := len(m.items) totalItems := len(m.items)
visibleItems := len(m.VisibleItems()) visibleItems := len(m.VisibleItems())
plural := "" var itemName string
if visibleItems != 1 { if visibleItems != 1 {
plural = "s" itemName = m.itemNamePlural
} else {
itemName = m.itemNameSingular
} }
itemsDisplay := fmt.Sprintf("%d %s", visibleItems, itemName)
if m.filterState == Filtering { if m.filterState == Filtering {
// Filter results // Filter results
if visibleItems == 0 { if visibleItems == 0 {
status = m.Styles.StatusEmpty.Render("Nothing matched") status = m.Styles.StatusEmpty.Render("Nothing matched")
} else { } else {
status = fmt.Sprintf("%d item%s", visibleItems, plural) status = itemsDisplay
} }
} else if len(m.items) == 0 { } else if len(m.items) == 0 {
// Not filtering: no items. // Not filtering: no items.
status = m.Styles.StatusEmpty.Render("No items") status = m.Styles.StatusEmpty.Render("No " + m.itemNamePlural)
} else { } else {
// Normal // Normal
filtered := m.FilterState() == FilterApplied filtered := m.FilterState() == FilterApplied
@ -1073,7 +1094,7 @@ func (m Model) statusView() string {
status += fmt.Sprintf("“%s” ", f) status += fmt.Sprintf("“%s” ", f)
} }
status += fmt.Sprintf("%d item%s", visibleItems, plural) status += itemsDisplay
} }
numFiltered := totalItems - visibleItems numFiltered := totalItems - visibleItems
@ -1117,7 +1138,7 @@ func (m Model) populatedView() string {
if m.filterState == Filtering { if m.filterState == Filtering {
return "" return ""
} }
return m.Styles.NoItems.Render("No items found.") return m.Styles.NoItems.Render("No " + m.itemNamePlural + " found.")
} }
if len(items) > 0 { if len(items) > 0 {

74
list/list_test.go Normal file
View File

@ -0,0 +1,74 @@
package list
import (
"fmt"
"io"
"strings"
"testing"
tea "github.com/charmbracelet/bubbletea"
)
type item string
func (i item) FilterValue() string { return "" }
type itemDelegate struct{}
func (d itemDelegate) Height() int { return 1 }
func (d itemDelegate) Spacing() int { return 0 }
func (d itemDelegate) Update(msg tea.Msg, m *Model) tea.Cmd { return nil }
func (d itemDelegate) Render(w io.Writer, m Model, index int, listItem Item) {
i, ok := listItem.(item)
if !ok {
return
}
str := fmt.Sprintf("%d. %s", index+1, i)
fmt.Fprint(w, m.Styles.TitleBar.Render(str))
}
func TestStatusBarItemName(t *testing.T) {
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10)
expected := "2 items"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
list.SetItems([]Item{item("foo")})
expected = "1 item"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
}
func TestStatusBarWithoutItems(t *testing.T) {
list := New([]Item{}, itemDelegate{}, 10, 10)
expected := "No items"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
}
func TestCustomStatusBarItemName(t *testing.T) {
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10)
list.SetStatusBarItemName("connection", "connections")
expected := "2 connections"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
list.SetItems([]Item{item("foo")})
expected = "1 connection"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
list.SetItems([]Item{})
expected = "No connections"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
}
}