mirror of
https://github.com/charmbracelet/gum
synced 2024-05-03 15:03:20 +02:00
feat(table): gum table for tabular data
This commit is contained in:
parent
a82d5af1e8
commit
bdd86d5fbc
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/alecthomas/kong v0.6.1
|
||||
github.com/alecthomas/mango-kong v0.1.0
|
||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221006105051-f406999cba69
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f
|
||||
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da
|
||||
github.com/charmbracelet/lipgloss v0.6.0
|
||||
github.com/mattn/go-runewidth v0.0.14
|
||||
|
|
14
go.sum
14
go.sum
|
@ -13,21 +13,14 @@ github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZs
|
|||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
||||
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
|
||||
github.com/charmbracelet/bubbles v0.14.1-0.20220926062606-e857875f2a75 h1:rjgidQdLMPMe7wQOs+ki2cD69keSKd8nZFPGIDxv4ZI=
|
||||
github.com/charmbracelet/bubbles v0.14.1-0.20220926062606-e857875f2a75/go.mod h1:5rZgJTHmgWISQnxnzzIJtQt3GC1bfJfNmr4SEtRDtTQ=
|
||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a h1:/prXWlDbR4CWT1FaTvkU8WhLfpZv3eOrN9PtL8oDdDU=
|
||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a/go.mod h1:5rZgJTHmgWISQnxnzzIJtQt3GC1bfJfNmr4SEtRDtTQ=
|
||||
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
|
||||
github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24=
|
||||
github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0=
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221006105051-f406999cba69 h1:GpZktjqyEQjuvFtFb0UlMlbZCOwOhk/bpKb6+quLz+E=
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221006105051-f406999cba69/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f h1:mbSd0Sdm2wXUWtXJ81o86G9V+9IhddX0qQcGK6bMbKo=
|
||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da h1:FGz53GWQRiKQ/5xUsoCCkewSQIC7u81Scaxx2nUy3nM=
|
||||
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da/go.mod h1:HXz79SMFnF9arKxqeoHWxmo1BhplAH7wehlRhKQIL94=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
|
||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
|
@ -58,7 +51,6 @@ github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P
|
|||
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/mango v0.1.1-0.20220205060214-77e2058169ab h1:m7QFONkzLK0fVXCjwX5tANcnj1yXxTnYQtnfJiY3tcA=
|
||||
|
@ -70,7 +62,6 @@ github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
|
|||
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
|
||||
github.com/muesli/termenv v0.11.0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI=
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
|
@ -101,7 +92,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
25
gum.go
25
gum.go
|
@ -11,8 +11,10 @@ import (
|
|||
"github.com/charmbracelet/gum/input"
|
||||
"github.com/charmbracelet/gum/join"
|
||||
"github.com/charmbracelet/gum/man"
|
||||
"github.com/charmbracelet/gum/pager"
|
||||
"github.com/charmbracelet/gum/spin"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
"github.com/charmbracelet/gum/table"
|
||||
"github.com/charmbracelet/gum/write"
|
||||
)
|
||||
|
||||
|
@ -100,6 +102,12 @@ type Gum struct {
|
|||
//
|
||||
Join join.Options `cmd:"" help:"Join text vertically or horizontally"`
|
||||
|
||||
// Pager provides a shell script interface for the viewport bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/viewport
|
||||
//
|
||||
// It allows the user to scroll through content like a pager.
|
||||
Pager pager.Options `cmd:"" help:"Scroll through a file"`
|
||||
|
||||
// Spin provides a shell script interface for the spinner bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/spinner
|
||||
//
|
||||
|
@ -142,6 +150,23 @@ type Gum struct {
|
|||
//
|
||||
Style style.Options `cmd:"" help:"Apply coloring, borders, spacing to text"`
|
||||
|
||||
// Table provides a shell script interface for the table bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/table
|
||||
//
|
||||
// It is useful to render tabular (CSV) data in a terminal and allows
|
||||
// the user to select a row from the table.
|
||||
//
|
||||
// Let's render a table of gum flavors:
|
||||
//
|
||||
// $ gum table <<< "Flavor,Price\nStrawberry,$0.50\nBanana,$0.99\nCherry,$0.75"
|
||||
//
|
||||
// Flavor Price
|
||||
// Strawberry $0.50
|
||||
// Banana $0.99
|
||||
// Cherry $0.75
|
||||
//
|
||||
Table table.Options `cmd:"" help:"Render a table of data"`
|
||||
|
||||
// Write provides a shell script interface for the text area bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/textarea
|
||||
//
|
||||
|
|
|
@ -10,13 +10,8 @@ import (
|
|||
|
||||
// Read reads input from an stdin pipe.
|
||||
func Read() (string, error) {
|
||||
stat, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to stat stdin: %w", err)
|
||||
}
|
||||
|
||||
if stat.Mode()&os.ModeNamedPipe == 0 && stat.Size() == 0 {
|
||||
return "", nil
|
||||
if IsEmpty() {
|
||||
return "", fmt.Errorf("stdin is empty")
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
@ -35,3 +30,17 @@ func Read() (string, error) {
|
|||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// IsEmpty returns whether stdin is empty.
|
||||
func IsEmpty() bool {
|
||||
stat, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if stat.Mode()&os.ModeNamedPipe == 0 && stat.Size() == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
42
pager/command.go
Normal file
42
pager/command.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package pager
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for the viewport bubble.
|
||||
// https://github.com/charmbracelet/bubbles/viewport
|
||||
func (o Options) Run() error {
|
||||
vp := viewport.New(o.Style.Width, o.Style.Height)
|
||||
vp.Style = o.Style.ToLipgloss()
|
||||
var err error
|
||||
|
||||
if o.Content == "" {
|
||||
o.Content, err = stdin.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
renderer, err := glamour.NewTermRenderer(
|
||||
glamour.WithWordWrap(80),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
md, err := renderer.Render(o.Content)
|
||||
vp.SetContent(md)
|
||||
|
||||
model := model{
|
||||
viewport: vp,
|
||||
helpStyle: o.HelpStyle.ToLipgloss(),
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tea.NewProgram(model, tea.WithAltScreen()).Start()
|
||||
}
|
10
pager/options.go
Normal file
10
pager/options.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package pager
|
||||
|
||||
import "github.com/charmbracelet/gum/style"
|
||||
|
||||
// Options are the options for the pager.
|
||||
type Options struct {
|
||||
Style style.Styles `embed:"" help:"Style the pager" envprefix:"GUM_PAGER_"`
|
||||
HelpStyle style.Styles `embed:"" prefix:"help." help:"Style the help text" set:"defaultForeground=241" envprefix:"GUM_PAGER_HELP_"`
|
||||
Content string `arg:"" optional:"" help:"Display content to scroll"`
|
||||
}
|
39
pager/pager.go
Normal file
39
pager/pager.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Package pager provides a pager (similar to less) for the terminal.
|
||||
//
|
||||
// $ cat file.txt | gum page
|
||||
package pager
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
viewport viewport.Model
|
||||
helpStyle lipgloss.Style
|
||||
}
|
||||
|
||||
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.viewport.Width = msg.Width
|
||||
m.viewport.Height = msg.Height - 1
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q", "ctrl+c", "esc":
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
var cmd tea.Cmd
|
||||
m.viewport, cmd = m.viewport.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
return m.viewport.View() + "\n" + m.helpStyle.Render("↑/↓: Navigate • q: Quit")
|
||||
}
|
5
table/comma.csv
Normal file
5
table/comma.csv
Normal file
|
@ -0,0 +1,5 @@
|
|||
Bubble Gum,Price,Ingredients
|
||||
Strawberry,$0.88,"Water,Sugar"
|
||||
Guava,$1.00,"Guava Flavoring,Food Coloring,Xanthan Gum"
|
||||
Orange,$0.99,"Sugar,Dextrose,Glucose"
|
||||
Cinnamon,$0.50,"Cin""na""mon"
|
|
111
table/command.go
Normal file
111
table/command.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package table
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kong"
|
||||
"github.com/charmbracelet/bubbles/table"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/mattn/go-runewidth"
|
||||
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for rendering tabular data (CSV)
|
||||
func (o Options) Run() error {
|
||||
var reader *csv.Reader
|
||||
if o.File != "" {
|
||||
file, err := os.OpenFile(o.File, os.O_RDONLY, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find file at path %s", o.File)
|
||||
}
|
||||
reader = csv.NewReader(file)
|
||||
} else {
|
||||
if stdin.IsEmpty() {
|
||||
return fmt.Errorf("no data provided")
|
||||
}
|
||||
reader = csv.NewReader(os.Stdin)
|
||||
}
|
||||
|
||||
separatorRunes := []rune(o.Separator)
|
||||
if len(separatorRunes) != 1 {
|
||||
return fmt.Errorf("separator must be single character")
|
||||
}
|
||||
reader.Comma = separatorRunes[0]
|
||||
|
||||
var columnNames []string
|
||||
var err error
|
||||
// If no columns are provided we'll use the first row of the CSV as the
|
||||
// column names.
|
||||
if len(o.Columns) <= 0 {
|
||||
columnNames, err = reader.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse columns")
|
||||
}
|
||||
} else {
|
||||
columnNames = o.Columns
|
||||
}
|
||||
|
||||
data, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid data provided")
|
||||
}
|
||||
var columns []table.Column
|
||||
|
||||
for i, title := range columnNames {
|
||||
width := runewidth.StringWidth(title)
|
||||
if len(o.Widths) > i {
|
||||
width = o.Widths[i]
|
||||
}
|
||||
columns = append(columns, table.Column{
|
||||
Title: title,
|
||||
Width: width,
|
||||
})
|
||||
}
|
||||
|
||||
defaultStyles := table.DefaultStyles()
|
||||
|
||||
styles := table.Styles{
|
||||
Cell: defaultStyles.Cell.Inherit(o.CellStyle.ToLipgloss()),
|
||||
Header: defaultStyles.Header.Inherit(o.HeaderStyle.ToLipgloss()),
|
||||
Selected: defaultStyles.Selected.Inherit(o.SelectedStyle.ToLipgloss()),
|
||||
}
|
||||
|
||||
var rows []table.Row
|
||||
for _, row := range data {
|
||||
rows = append(rows, table.Row(row))
|
||||
}
|
||||
|
||||
table := table.New(
|
||||
table.WithColumns(columns),
|
||||
table.WithFocused(true),
|
||||
table.WithHeight(o.Height),
|
||||
table.WithRows(rows),
|
||||
table.WithStyles(styles),
|
||||
)
|
||||
|
||||
tm, err := tea.NewProgram(model{table: table}, tea.WithOutput(os.Stderr)).StartReturningModel()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start tea program: %w", err)
|
||||
}
|
||||
|
||||
if tm == nil {
|
||||
return fmt.Errorf("failed to get selection")
|
||||
}
|
||||
|
||||
m := tm.(model)
|
||||
fmt.Println(strings.Join([]string(m.selected), string(o.Separator)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BeforeReset hook. Used to unclutter style flags.
|
||||
func (o Options) BeforeReset(ctx *kong.Context) error {
|
||||
style.HideFlags(ctx)
|
||||
return nil
|
||||
}
|
19
table/example.csv
Normal file
19
table/example.csv
Normal file
|
@ -0,0 +1,19 @@
|
|||
Bubble Gum Flavor,Price
|
||||
Strawberry,$0.99
|
||||
Cherry,$0.50
|
||||
Banana,$0.75
|
||||
Orange,$0.25
|
||||
Lemon,$0.50
|
||||
Lime,$0.50
|
||||
Grape,$0.50
|
||||
Watermelon,$0.50
|
||||
Pineapple,$0.50
|
||||
Blueberry,$0.50
|
||||
Raspberry,$0.50
|
||||
Cranberry,$0.50
|
||||
Peach,$0.50
|
||||
Apple,$0.50
|
||||
Mango,$0.50
|
||||
Pomegranate,$0.50
|
||||
Coconut,$0.50
|
||||
Cinnamon,$0.50
|
|
19
table/invalid.csv
Normal file
19
table/invalid.csv
Normal file
|
@ -0,0 +1,19 @@
|
|||
Bubble Gum Flavor
|
||||
Strawberry,$0.99
|
||||
Cherry,$0.50
|
||||
Banana,$0.75
|
||||
Orange
|
||||
Lemon,$0.50
|
||||
Lime,$0.50
|
||||
Grape,$0.50
|
||||
Watermelon,$0.50
|
||||
Pineapple,$0.50
|
||||
Blueberry,$0.50
|
||||
Raspberry,$0.50
|
||||
Cranberry,$0.50
|
||||
Peach,$0.50
|
||||
Apple,$0.50
|
||||
Mango,$0.50
|
||||
Pomegranate,$0.50
|
||||
Coconut,$0.50
|
||||
Cinnamon,$0.50
|
|
15
table/options.go
Normal file
15
table/options.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package table
|
||||
|
||||
import "github.com/charmbracelet/gum/style"
|
||||
|
||||
// Options is the customization options for the table command.
|
||||
type Options struct {
|
||||
Separator string `short:"s" help:"Row separator" default:","`
|
||||
Columns []string `short:"c" help:"Column names"`
|
||||
Widths []int `short:"w" help:"Column widths"`
|
||||
Height int `help:"Table height" default:"10"`
|
||||
CellStyle style.Styles `embed:"" prefix:"cell." envprefix:"GUM_TABLE_CELL"`
|
||||
HeaderStyle style.Styles `embed:"" prefix:"header." envprefix:"GUM_TABLE_HEADER"`
|
||||
SelectedStyle style.Styles `embed:"" prefix:"selected." set:"defaultForeground=212" envprefix:"GUM_TABLE_SELECTED"`
|
||||
File string `short:"f" help:"file path" default:""`
|
||||
}
|
58
table/table.go
Normal file
58
table/table.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Package table provides a shell script interface for the table bubble.
|
||||
// https://github.com/charmbracelet/bubbles/tree/master/table
|
||||
//
|
||||
// It is useful to render tabular (CSV) data in a terminal and allows
|
||||
// the user to select a row from the table.
|
||||
//
|
||||
// Let's render a table of gum flavors:
|
||||
//
|
||||
// $ gum table <<< "Flavor,Price\nStrawberry,$0.50\nBanana,$0.99\nCherry,$0.75"
|
||||
//
|
||||
// Flavor Price
|
||||
// Strawberry $0.50
|
||||
// Banana $0.99
|
||||
// Cherry $0.75
|
||||
//
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/table"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
table table.Model
|
||||
selected table.Row
|
||||
quitting bool
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "enter":
|
||||
m.selected = m.table.SelectedRow()
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
case "ctrl+c", "q":
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
}
|
||||
|
||||
m.table, cmd = m.table.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.quitting {
|
||||
return ""
|
||||
}
|
||||
return m.table.View()
|
||||
}
|
Loading…
Reference in a new issue