bubbles icon indicating copy to clipboard operation
bubbles copied to clipboard

What about Table widget?

Open hyorigo opened this issue 5 years ago • 4 comments

Hi guys,

Do you have any plan to implement Table widgets for Bubble Tea? Or a little hack with the "Viewpoint" widget to support simple tables?

Thanks for your great works!

hyorigo avatar Oct 21 '20 05:10 hyorigo

Hi! Tables are pretty simple in Bubble Tea since they're just strings. Here's some example code we just pushed that shows how you can render a table in a viewport using Glamour.

Glamour Example

That said, we do plan in making an interactive table widget. In the meantime, here's some example code for an interactive table using olekukonko/tablewriter:

Interactive Table Example

package main

import (
	"fmt"
	"os"
	"strings"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/muesli/termenv"
	"github.com/olekukonko/tablewriter"
)

var (
	term = termenv.ColorProfile()

	// The data for our table
	data = [][]string{
		{"A Fistful of Dollars", "Western", "1964"},
		{"For a few Dollars More", "Western", "1965"},
		{"The Good, the Bad and the Ugly", "Western", "1966"},
	}
)

type model int

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.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			return m, tea.Quit
		case "k", "up":
			m--
			if int(m) < 0 {
				m = model(len(data) - 1)
			}
		case "j", "down":
			m++
			if int(m) > len(data) {
				m = 0
			}
		}
	}
	return m, nil
}

func (m model) View() string {
	s := strings.Builder{}

	// Set table header
	table := tablewriter.NewWriter(&s)
	table.SetHeader([]string{"Name", "Genre", "Year"})

	// Add data to table
	for i, v := range data {
		if int(m) == i {
			// Color active item
			c := tablewriter.Colors{tablewriter.FgBlackColor, tablewriter.BgWhiteColor}
			var colors []tablewriter.Colors
			for range v {
				colors = append(colors, c)
			}
			table.Rich(v, colors)
		} else {
			table.Append(v)
		}

	}

	table.Render()
	s.WriteString(termenv.String(" ↑/↓: Choose • q: Quit").Foreground(term.Color("241")).String())
	return s.String()
}

func main() {
	if err := tea.NewProgram(model(0)).Start(); err != nil {
		fmt.Printf("Oh no! %v\n", err)
		os.Exit(1)
	}
}

There are plenty of other table drawing packages that would work too. Let us know if you have any questions.

meowgorithm avatar Oct 21 '20 21:10 meowgorithm

Thanks for your help!

Fix a typo in the code snippet:

			if int(m) > len(data) {
				m = 0
			}

->

			if int(m) >= len(data) {
				m = 0
			}

However, I do hope you guys can support a formal table widget like the viewport in Glamour, the border lines of olekukonko/tablewriter looks really awkward.

hyorigo avatar Oct 25 '20 16:10 hyorigo

Thanks for that. Glamour actually uses olekukonko/tablewriter but with custom glyphs for borders. Here’s the implementation and here are the glyphs.

Anyway, I'll let you know when we build a component for this; it will more than likely be based on the code above.

meowgorithm avatar Oct 25 '20 19:10 meowgorithm

Is there a simple way to combine this example with a pager or the possibility to scroll the content of the table (and keep the header fixed to top) in AltMode? Because if there are too many items in the table, the selection/display is broken.

rverton avatar Apr 29 '21 21:04 rverton

Table widget is now implemented https://github.com/charmbracelet/bubbles/tree/master/table

maaslalani avatar Aug 17 '22 22:08 maaslalani

@maaslalani Great works! what about a little intro and demo gif in the README file?

hyorigo avatar Aug 19 '22 01:08 hyorigo

@hyorigo Introduction is here (https://github.com/charmbracelet/bubbles/releases/tag/v0.14.0) and demo gif is now in the README (https://github.com/charmbracelet/bubbles)!

maaslalani avatar Sep 06 '22 17:09 maaslalani