mirror of
https://github.com/charmbracelet/gum
synced 2024-06-07 16:22:17 +02:00
feat(choose): Add paginator to chooser
Display paginator only if number of options > height specified
This commit is contained in:
parent
a26995e17f
commit
4d98a8fa6f
|
@ -14,6 +14,7 @@ package choose
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/paginator"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/mattn/go-runewidth"
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
|
|
||||||
type model struct {
|
type model struct {
|
||||||
height int
|
height int
|
||||||
page int
|
|
||||||
cursor string
|
cursor string
|
||||||
selectedPrefix string
|
selectedPrefix string
|
||||||
unselectedPrefix string
|
unselectedPrefix string
|
||||||
|
@ -31,6 +31,7 @@ type model struct {
|
||||||
index int
|
index int
|
||||||
limit int
|
limit int
|
||||||
numSelected int
|
numSelected int
|
||||||
|
paginator paginator.Model
|
||||||
|
|
||||||
// styles
|
// styles
|
||||||
cursorStyle lipgloss.Style
|
cursorStyle lipgloss.Style
|
||||||
|
@ -51,27 +52,24 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
|
start, end := m.paginator.GetSliceBounds(len(m.items))
|
||||||
switch keypress := msg.String(); keypress {
|
switch keypress := msg.String(); keypress {
|
||||||
case "down", "j", "ctrl+n":
|
case "down", "j", "ctrl+n":
|
||||||
m.index = (m.index + 1) % len(m.items)
|
m.index = clamp(m.index+1, 0, len(m.items)-1)
|
||||||
m.page = m.index / m.height
|
if m.index >= end {
|
||||||
|
m.paginator.NextPage()
|
||||||
|
}
|
||||||
case "up", "k", "ctrl+p":
|
case "up", "k", "ctrl+p":
|
||||||
m.index = (m.index - 1 + len(m.items)) % len(m.items)
|
m.index = clamp(m.index-1, 0, len(m.items)-1)
|
||||||
m.page = m.index / m.height
|
if m.index <= start {
|
||||||
|
m.paginator.PrevPage()
|
||||||
|
}
|
||||||
case "right", "l", "ctrl+f":
|
case "right", "l", "ctrl+f":
|
||||||
if m.index+m.height < len(m.items) {
|
m.index = clamp(m.index+m.height, 0, len(m.items)-1)
|
||||||
m.index += m.height
|
m.paginator.NextPage()
|
||||||
} else {
|
|
||||||
if m.page < len(m.items)/m.height {
|
|
||||||
m.index = len(m.items) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.page = m.index / m.height
|
|
||||||
case "left", "h", "ctrl+b":
|
case "left", "h", "ctrl+b":
|
||||||
if m.index-m.height >= 0 {
|
m.index = clamp(m.index-m.height, 0, len(m.items)-1)
|
||||||
m.index -= m.height
|
m.paginator.PrevPage()
|
||||||
}
|
|
||||||
m.page = m.index / m.height
|
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
m.quitting = true
|
m.quitting = true
|
||||||
return m, tea.Quit
|
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 {
|
func (m model) View() string {
|
||||||
|
@ -108,7 +108,8 @@ func (m model) View() string {
|
||||||
|
|
||||||
var s strings.Builder
|
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 {
|
if i == m.index%m.height {
|
||||||
s.WriteString(m.cursorStyle.Render(m.cursor))
|
s.WriteString(m.cursorStyle.Render(m.cursor))
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,9 +123,18 @@ func (m model) View() string {
|
||||||
} else {
|
} else {
|
||||||
s.WriteString(m.itemStyle.Render(m.unselectedPrefix + item.text))
|
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()
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,15 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/paginator"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/gum/internal/stdin"
|
"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
|
// 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)
|
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{
|
m, err := tea.NewProgram(model{
|
||||||
height: o.Height,
|
height: o.Height,
|
||||||
cursor: o.Cursor,
|
cursor: o.Cursor,
|
||||||
|
@ -48,6 +70,7 @@ func (o Options) Run() error {
|
||||||
cursorPrefix: o.CursorPrefix,
|
cursorPrefix: o.CursorPrefix,
|
||||||
items: items,
|
items: items,
|
||||||
limit: o.Limit,
|
limit: o.Limit,
|
||||||
|
paginator: pager,
|
||||||
cursorStyle: o.CursorStyle.ToLipgloss(),
|
cursorStyle: o.CursorStyle.ToLipgloss(),
|
||||||
itemStyle: o.ItemStyle.ToLipgloss(),
|
itemStyle: o.ItemStyle.ToLipgloss(),
|
||||||
selectedItemStyle: o.SelectedItemStyle.ToLipgloss(),
|
selectedItemStyle: o.SelectedItemStyle.ToLipgloss(),
|
||||||
|
|
Loading…
Reference in a new issue