mirror of
https://github.com/charmbracelet/gum
synced 2024-05-17 13:46:42 +02:00
feat: gum choose
, pick from a list of choices
gum choose allows the user to be prompted for a choice from a list of choices. For example, let's ask the user to pick a card from a deck. gum choose --height 15 {Ace,King,Queen,Jack,Ten,Nine,Eight,Seven,Six,Five,Four,Three,Two}" of "{Spades,Hearts,Clubs,Diamonds}
This commit is contained in:
parent
dab3792d5f
commit
46ddc28ae5
13
README.md
13
README.md
|
@ -28,7 +28,7 @@ gum spin --title "Taking a nap..." --color 212 -- sleep 5
|
|||
find . -type f | gum filter
|
||||
```
|
||||
|
||||
The following example is running from a [single Bash script](./examples/demo.sh).
|
||||
The following example is running from a [single bash script](./examples/demo.sh).
|
||||
|
||||
<img src="https://stuff.charm.sh/gum/gum.gif" width="900" alt="Shell running the Gum examples/demo.sh script">
|
||||
|
||||
|
@ -101,6 +101,17 @@ echo Cherry >> flavors.text
|
|||
cat flavors.text | gum filter > selection.text
|
||||
```
|
||||
|
||||
#### Choose
|
||||
|
||||
Ask your users to choose an option from a list of choices.
|
||||
|
||||
|
||||
```bash
|
||||
echo "Pick a card, any card..."
|
||||
CARD=$(gum choose --height 15 {{A,K,Q,J},{10..2}}" "{♠,♥,♣,♦})
|
||||
echo "Was your card the $CARD?"
|
||||
```
|
||||
|
||||
#### Progress
|
||||
|
||||
Display a progress bar while loading. The following command will display a
|
||||
|
|
101
choose/choose.go
Normal file
101
choose/choose.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
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
|
||||
}
|
47
choose/command.go
Normal file
47
choose/command.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package choose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for choosing between different through
|
||||
// options.
|
||||
func (o Options) Run() {
|
||||
items := []list.Item{}
|
||||
for _, option := range o.Options {
|
||||
if option == "" {
|
||||
continue
|
||||
}
|
||||
items = append(items, item(option))
|
||||
}
|
||||
|
||||
const defaultWidth = 20
|
||||
|
||||
id := itemDelegate{
|
||||
indicator: o.Indicator,
|
||||
indicatorStyle: lipgloss.NewStyle().Foreground(lipgloss.Color(o.IndicatorColor)),
|
||||
itemStyle: lipgloss.NewStyle().Padding(0, runewidth.StringWidth(o.Indicator)).Foreground(lipgloss.Color(o.UnselectedColor)),
|
||||
selectedItemStyle: lipgloss.NewStyle().Foreground(lipgloss.Color(o.SelectedColor)),
|
||||
}
|
||||
|
||||
l := list.New(items, id, defaultWidth, o.Height)
|
||||
l.SetShowTitle(false)
|
||||
l.SetShowStatusBar(false)
|
||||
l.SetFilteringEnabled(false)
|
||||
l.SetShowHelp(false)
|
||||
l.SetShowPagination(!o.HidePagination)
|
||||
|
||||
m, err := tea.NewProgram(model{list: l}, tea.WithOutput(os.Stderr)).StartReturningModel()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error running program:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(m.(model).choice)
|
||||
}
|
13
choose/options.go
Normal file
13
choose/options.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package choose
|
||||
|
||||
// Options is the customization options for the choose command.
|
||||
type Options struct {
|
||||
Options []string `arg:"" optional:"" help:"Options to choose from."`
|
||||
|
||||
Height int `help:"Height of the list." default:"10"`
|
||||
HidePagination bool `help:"Hide pagination." default:"false"`
|
||||
Indicator string `help:"Prefix to show on selected item" default:"> "`
|
||||
IndicatorColor string `help:"Indicator foreground color" default:"212"`
|
||||
SelectedColor string `help:"Selected item foreground color" default:"212"`
|
||||
UnselectedColor string `help:"Unselected item foreground color" default:""`
|
||||
}
|
14
gum.go
14
gum.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/gum/choose"
|
||||
"github.com/charmbracelet/gum/filter"
|
||||
"github.com/charmbracelet/gum/input"
|
||||
"github.com/charmbracelet/gum/join"
|
||||
|
@ -45,6 +46,19 @@ type Gum struct {
|
|||
//
|
||||
Filter filter.Options `cmd:"" help:"Filter options through fuzzy search."`
|
||||
|
||||
// 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"
|
||||
//
|
||||
Choose choose.Options `cmd:"" help:"Choose from a list of options."`
|
||||
|
||||
// Spin provides a shell script interface for the spinner bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/spinner
|
||||
//
|
||||
|
|
8
main.go
8
main.go
|
@ -1,6 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
|
@ -25,6 +27,12 @@ func main() {
|
|||
gum.Write.Run()
|
||||
case "filter":
|
||||
gum.Filter.Run()
|
||||
case "choose":
|
||||
input, _ := stdin.Read()
|
||||
gum.Choose.Options = strings.Split(input, "\n")
|
||||
gum.Choose.Run()
|
||||
case "choose <options>":
|
||||
gum.Choose.Run()
|
||||
case "spin <command>":
|
||||
gum.Spin.Run()
|
||||
case "progress":
|
||||
|
|
Loading…
Reference in a new issue