From 4d98a8fa6f982f638cf1f4b163395f30b1470d36 Mon Sep 17 00:00:00 2001 From: Maas Lalani Date: Wed, 13 Jul 2022 15:49:14 -0400 Subject: [PATCH] feat(choose): Add paginator to chooser Display paginator only if number of options > height specified --- choose/choose.go | 50 ++++++++++++++++++++++++++++------------------- choose/command.go | 23 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/choose/choose.go b/choose/choose.go index 6d947cc..61445b1 100644 --- a/choose/choose.go +++ b/choose/choose.go @@ -14,6 +14,7 @@ package choose import ( "strings" + "github.com/charmbracelet/bubbles/paginator" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/mattn/go-runewidth" @@ -21,7 +22,6 @@ import ( type model struct { height int - page int cursor string selectedPrefix string unselectedPrefix string @@ -31,6 +31,7 @@ type model struct { index int limit int numSelected int + paginator paginator.Model // styles cursorStyle lipgloss.Style @@ -51,27 +52,24 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil case tea.KeyMsg: + start, end := m.paginator.GetSliceBounds(len(m.items)) switch keypress := msg.String(); keypress { case "down", "j", "ctrl+n": - m.index = (m.index + 1) % len(m.items) - m.page = m.index / m.height + m.index = clamp(m.index+1, 0, len(m.items)-1) + if m.index >= end { + m.paginator.NextPage() + } case "up", "k", "ctrl+p": - m.index = (m.index - 1 + len(m.items)) % len(m.items) - m.page = m.index / m.height + m.index = clamp(m.index-1, 0, len(m.items)-1) + if m.index <= start { + m.paginator.PrevPage() + } case "right", "l", "ctrl+f": - if m.index+m.height < len(m.items) { - m.index += m.height - } else { - if m.page < len(m.items)/m.height { - m.index = len(m.items) - 1 - } - } - m.page = m.index / m.height + m.index = clamp(m.index+m.height, 0, len(m.items)-1) + m.paginator.NextPage() case "left", "h", "ctrl+b": - if m.index-m.height >= 0 { - m.index -= m.height - } - m.page = m.index / m.height + m.index = clamp(m.index-m.height, 0, len(m.items)-1) + m.paginator.PrevPage() case "ctrl+c": m.quitting = true return m, tea.Quit @@ -98,7 +96,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } - return m, nil + var cmd tea.Cmd + m.paginator, cmd = m.paginator.Update(msg) + return m, cmd } func (m model) View() string { @@ -108,7 +108,8 @@ func (m model) View() string { var s strings.Builder - for i, item := range m.items[clamp(m.page*m.height, 0, len(m.items)):clamp((m.page+1)*m.height, 0, len(m.items))] { + start, end := m.paginator.GetSliceBounds(len(m.items)) + for i, item := range m.items[start:end] { if i == m.index%m.height { s.WriteString(m.cursorStyle.Render(m.cursor)) } else { @@ -122,9 +123,18 @@ func (m model) View() string { } else { s.WriteString(m.itemStyle.Render(m.unselectedPrefix + item.text)) } - s.WriteRune('\n') + if i != m.height { + s.WriteRune('\n') + } } + if m.paginator.TotalPages <= 1 { + return s.String() + } + + s.WriteString(strings.Repeat("\n", m.height-m.paginator.ItemsOnPage(len(m.items))+1)) + s.WriteString(" " + m.paginator.View()) + return s.String() } diff --git a/choose/command.go b/choose/command.go index afe8c8e..bf269b0 100644 --- a/choose/command.go +++ b/choose/command.go @@ -6,8 +6,15 @@ import ( "os" "strings" + "github.com/charmbracelet/bubbles/paginator" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/gum/internal/stdin" + "github.com/charmbracelet/lipgloss" +) + +var ( + subduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"}) + verySubduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"}) ) // Run provides a shell script interface for choosing between different through @@ -40,6 +47,21 @@ func (o Options) Run() error { o.Limit = len(o.Options) } + // Use the pagination model to display the current and total number of + // pages. + pager := paginator.New() + pager.SetTotalPages((len(items) + o.Height - 1) / o.Height) + pager.PerPage = o.Height + pager.Type = paginator.Dots + pager.ActiveDot = subduedStyle.Render("•") + pager.InactiveDot = verySubduedStyle.Render("•") + + // Disable Keybindings since we will control it ourselves. + pager.UseHLKeys = false + pager.UseLeftRightKeys = false + pager.UseJKKeys = false + pager.UsePgUpPgDownKeys = false + m, err := tea.NewProgram(model{ height: o.Height, cursor: o.Cursor, @@ -48,6 +70,7 @@ func (o Options) Run() error { cursorPrefix: o.CursorPrefix, items: items, limit: o.Limit, + paginator: pager, cursorStyle: o.CursorStyle.ToLipgloss(), itemStyle: o.ItemStyle.ToLipgloss(), selectedItemStyle: o.SelectedItemStyle.ToLipgloss(),