gum/choose/choose.go

113 lines
2.4 KiB
Go

// Package choose provides an interface to choose one option from a given list
// of options. The options can be provided as (new-line separated) stdin or a
// list of arguments.
//
// It is different from the filter command as it does not provide a fuzzy
// finding input, so it is best used for smaller lists of options.
//
// Let's pick from a list of gum flavors:
//
// $ gum choose "Strawberry" "Banana" "Cherry"
//
package choose
import (
"fmt"
"io"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type model struct {
choice string
height int
indicator string
indicatorStyle lipgloss.Style
itemStyle lipgloss.Style
items []item
list list.Model
options []string
quitting bool
selectedItemStyle lipgloss.Style
}
type item string
func (i item) FilterValue() string { return "" }
type itemDelegate struct {
indicator string
indicatorStyle lipgloss.Style
itemStyle lipgloss.Style
selectedItemStyle lipgloss.Style
}
func (d itemDelegate) Height() int { return 1 }
func (d itemDelegate) Spacing() int { return 0 }
func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
i, ok := listItem.(item)
if !ok {
return
}
str := fmt.Sprintf("%s", i)
fn := d.itemStyle.Render
if index == m.Index() {
fn = func(s string) string {
return d.indicatorStyle.Render(d.indicator) + d.selectedItemStyle.Render(s)
}
}
fmt.Fprintf(w, fn(str))
}
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.list.SetWidth(msg.Width)
return m, nil
case tea.KeyMsg:
switch keypress := msg.String(); keypress {
case "ctrl+c":
m.quitting = true
return m, tea.Quit
case "enter":
m.quitting = true
i, ok := m.list.SelectedItem().(item)
if ok {
m.choice = string(i)
}
return m, tea.Quit
}
}
var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
return m, cmd
}
func (m model) View() string {
if m.quitting {
return ""
}
return m.list.View()
}
func clamp(min, max, val int) int {
if val < min {
return min
}
if val > max {
return max
}
return val
}