Giter VIP home page Giter VIP logo

bubble-table's People

Contributors

alswl avatar evertras avatar gairadzi avatar greglanthier avatar inkel avatar prgres avatar robertjli avatar robertkwiatkowski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

bubble-table's Issues

How to find out if filter input is currently active?

I use/keep the default / key of the bubble-table to activate the filter input line.

How can I check if the user is currently entering a filter?

Use-Case: I have a menu with many functions a user can select by pressing one key. But when the user enters a filter, I want to skip that menu handling code not to trigger any functions while a filter is entered.

Could be two new events (see: #60)

  1. UserEventFilterEnteringStarted
  2. UserEventFilterEnteringDone

FR: ESC to immediately remove any active filtering

I like using / to start the table filtering, but I have to enter filter edit mode, delete the filter, and press return to remove a filter.

How about supporting ESC to clear any filter?

The table movement key should/could continue to work while I still edit the filter—no need to press return to get back to table navigation mode. I can continue to alter the filter and navigate the table.

Sort by column

There should be a way to sort by column. This should include both a programmatic way and a way to navigate and select the column and apply sorting on the fly.

Index out of range panic when the highlighted row index is higher than the filtered row count

I'm using HighlightedRow() from inside the Update() function to access the detailed row data.

However, when filtering the table, I manage to create a panic:

  1. set Page size to 20
  2. select record 19
  3. filter the table to < 19 records => panic

Looks like the case, when the table size gets shorter because of filtering, and the highlighted row is out of sight, accessing it, leads to the crash.

Caught panic:

runtime error: index out of range [3] with length 1

Restoring terminal...

goroutine 1 [running]:
runtime/debug.Stack()
	/usr/local/go/src/runtime/debug/stack.go:24 +0x65
runtime/debug.PrintStack()
	/usr/local/go/src/runtime/debug/stack.go:16 +0x19
github.com/charmbracelet/bubbletea.(*Program).StartReturningModel.func3()
	/Users/robby/go/pkg/mod/github.com/charmbracelet/bubbletea@v0.20.0/tea.go:359 +0x95
panic({0x115faa0, 0xc003685ba8})
	/usr/local/go/src/runtime/panic.go:844 +0x258
github.com/evertras/bubble-table/table.Model.HighlightedRow({{0xc0035fda40, 0x4, 0x4}, {0xc003702000, 0x1a2, 0x220}, 0x1, {{{0xc00014e000, 0x2, 0x2}, ...}, ...}, ...})
	bubble-table/table/options.go:84 +0x16c

Allow some configurable indicator of missing data

Currently, missing data is rendered as a blank string. This makes it impossible to tell the difference between empty data (like "") and data that is actually missing.

There should be a configurable indicator for data that is actually missing and not just a zero value. In this case, both nil and missing the data in the RowData map entirely should show this value. The user should be able to style this indicator with lipgloss.

Related to #94

Pagination

Right now there is no upper bound on the number of rows, and there is no way to scroll down. There should be a way to paginate the rows in the view by specifying a page size. Note that the row data itself should not change, only the view should be affected.

Allow header to be hidden

There are some cases where the header should be invisible, particularly with CSV-style data that may not have relevant header names at all. The table should have a toggle to allow the header to be visible/hidden.

Related to #94

Better view tests

Right now the view just has a stupidly basic test... I'm honestly not sure what a good way to test this is, given the visual nature and complicated ANSI codes from styling.

If anyone has thoughts, post here! I'd rather avoid a big PR of fragile tests that ends up being rejected. <3

FlexColumn not showing anything

With

a.lt.licsTable = table.New(
[]table.Column{
	table.NewColumn(tcEmail, "Email", 20),
	table.NewColumn(tcEnd, "End", 20),
	table.NewColumn(tcPaidUntil, "Paid Until", 20),
	table.NewColumn(tcVersion, "Version", 20),
}).WithPageSize(10).SortByDesc(tcPaidUntil).WithRows(generateRows(a.lt))

I get:
Ohne Titel 3

But, with:

a.lt.licsTable = table.New(
[]table.Column{
	table.NewFlexColumn(tcEmail, "Email", 1),
	table.NewColumn(tcEnd, "End", 20),
	table.NewColumn(tcPaidUntil, "Paid Until", 20),
	table.NewColumn(tcVersion, "Version", 20),
}).WithPageSize(10).SortByDesc(tcPaidUntil).WithRows(generateRows(a.lt))

I get:
image

Is there anything special I need to take care of when using flex columns?

How to set page programmatically?

How can I switch to a specific page programmatically when pagination is used?

The 'pageFirst()' is not public.

However, when

  1. I load data into the table
  2. the user navigates forward/backward
  3. and I load new data

The table shows the current page, which might not exist because the new data is shorter.

'WithRows' doesn't reset the tables state, and there is no function to reset the tables' state.

Is the `limitStr` function unintentionally counting ANSI control characters when truncating strings?

Thank you for sharing bubble-table - its easy to use and full of great features.

I have a question about expected behaviour when it comes to how text is rendered in a cell.

I was trying to place a progress-bar using github.com/charmbracelet/bubbles/progress into a cell in a table using code that looks loosely like this:

	prog := progress.New(
		progress.WithScaledGradient("#FF7CCB", "#FDFF8C"),
		progress.WithWidth(18),
	)
	prog.Full = ':'
	prog.Empty = ' '
	m.table = table.New([]table.Column{
		table.NewFlexColumn("cpu", "CPU", 1),
	}).WithRows([]table.Row{
		table.NewRow(table.RowData{
			"cpu":      fmt.Sprintf("[%s]", prog.ViewAs(0.34)),
		}),
		table.NewRow(table.RowData{
			"cpu":      fmt.Sprintf("[%s]", prog.ViewAs(0.54)),
		}),
		table.NewRow(table.RowData{
			"cpu":      fmt.Sprintf("[%s]", prog.ViewAs(0.84)),
		}),
	}).WithBaseStyle(lipgloss.NewStyle().Align(lipgloss.Left)).Border(table.Border{}).HeaderStyle(lipgloss.NewStyle().Bold(true).Background(lipgloss.Color("#7D56F4"))).SortByAsc("name").
		WithTargetWidth(w + 4).Focused(true).WithNoPagination()

but kept running into problems where the progress bar text itself was being truncated prematurely. I suspect this is due to the string containing ANSI control sequences.

Using examples/updates/main.go to demonstrate if I make an edit like this:

diff --git a/examples/updates/main.go b/examples/updates/main.go
index 207a9ac..ab79a78 100644
--- a/examples/updates/main.go
+++ b/examples/updates/main.go
@@ -42,7 +42,7 @@ func NewModel() Model {
 func refreshDataCmd() tea.Msg {
        // This could come from some API or something
        return []*SomeData{
-               NewSomeData("abc"),
+               NewSomeData("\033[00;37mabc"),
                NewSomeData("def"),
                NewSomeData("123"),
                NewSomeData("ok"),

go run ./examples/updates renders something like this:

image

while the following code change:

diff --git a/examples/updates/main.go b/examples/updates/main.go
index 207a9ac..9a23628 100644
--- a/examples/updates/main.go
+++ b/examples/updates/main.go
@@ -42,7 +42,7 @@ func NewModel() Model {
 func refreshDataCmd() tea.Msg {
        // This could come from some API or something
        return []*SomeData{
-               NewSomeData("abc"),
+               NewSomeData("\033[00;37m\033[00;37mabc"),
                NewSomeData("def"),
                NewSomeData("123"),
                NewSomeData("ok"),

renders like this when you run go run ./examples/updates:

image

It looks like this is a result of how code in table/strlimit.go calculates the length of a string being considered for truncation.

if runewidth.StringWidth(str) > maxLen {
return runewidth.Truncate(str, maxLen-1, "") + "…"
}

If I make the following change

diff --git a/table/strlimit.go b/table/strlimit.go
index e21d97e..9c374d2 100644
--- a/table/strlimit.go
+++ b/table/strlimit.go
@@ -3,7 +3,7 @@ package table
 import (
        "strings"
 
-       "github.com/mattn/go-runewidth"
+       "github.com/charmbracelet/lipgloss"
 )
 
 func limitStr(str string, maxLen int) string {
@@ -16,8 +16,8 @@ func limitStr(str string, maxLen int) string {
                str = str[:newLineIndex] + "…"
        }
 
-       if runewidth.StringWidth(str) > maxLen {
-               return runewidth.Truncate(str, maxLen-1, "") + "…"
+       if lipgloss.Width(str) > maxLen {
+               return lipgloss.NewStyle().SetString(str).MaxWidth(maxLen-1).String() + "…"
        }
 
        return str

my use case of embedding ANSI control codes in text that is rendered in a cell in a table works as expected.

Does this use case - allowing user supplied ANSI control characters in cell contents - make sense?

Would this be something you'd consider accepting a PR for (assuming appropriate tests are included, of course)?

Search filters

We should be able to apply a search filter on a column and only display results that fit the filter. Probably just a simple substring match to start.

Get all rows

First of all, thanks for your amazing lib.

I need to get all rows to update data, please add a function to retrieve all rows or a function to clear the table rows and add new ones.

If you have solution for this please let me know.

Thank you in advanced

Feature request: have `Row` hold arbitrary data

Hi! Let me start by saying this is a great project, fabulous work! 👏🏽

I was looking at the docs because I wanted to know if it were possible to associate any arbitrary data other than the column values, but according to the Row documentation, that's not possible 😞

This got me thinking that perhaps the following would be a better definition for Row plus a new method:

type Row struct {
	Style lipgloss.Style
	Cells RowData
	Data  interface{}
}

func (r Row) WithData(data interface{}) Row {
	r.Data = data
	return r
}

(I'm leaving the unexported fields untouched)

It is a breaking change, but not too big IMO, and the benefits would be great, as I hope to show in the following example: say you have a struct like the following that you'd like to display as a table:

type Person struct {
	Name string
	Age  int
}

func (p Person) Delete() error { /* … */ }

Then in your UI, after some key is pressed, you would like to delete the selected rows from the table. As it currently is, it I call SelectedRows the return slice will only have the columns information for each row, and and would then need to use that for some logic within my app to find each Person. However, with my proposed changes, the code would then be:

for _, row := range table.SelectedRows() {
	p := row.Data.(Person)
	p.Delete() // sample method call
}

I was willing to send a PR with this change, but given the blast radius it might have, that is, renaming a field in Row and adding a new field with the same name as the existing one, I thought it'd be better to ask first 😸

PS: after writing this I think you could reduce the impact if instead of reusing the Data name for the field, just add a new one such as:

type Row struct {
	Style lipgloss.Style
	Data RowData
	Item  interface{}
}

Then you could even get rid of the ``Row.WithData` method I believe, although it's nice syntactic sugar.

Anyway, happy to discuss it!

Dynamically sized columns

Right now the columns have a fixed width. They should be able to grow/shrink based on an overall width, so that the table can grow into larger areas and shrink in a sane way if necessary. All the columns should keep their relative sizes, and maybe we'll need some minimums to be supplied on a per-column basis.

Customize Border Style

Currently you can customize the style of the rows and columns, but I was unsure how to customize the border style. I know that you can customize the glyphs used for the border itself, but I just wanted to customize the foreground and background colors for the border. Is that something that you are thinking of implementing, or can it be done via some method that I don't know of? Right now I am wrapping the whole table in a lipgloss.Style, but some of the inner borders stay at the default color, rather than the one that I specified in the style. Maybe it will work if implemented here instead 🤔 ?

feat: custom filter text

Hello again -- as shown in #115 as an example, my app layout includes a "command bar" that can both invoke commands and filter views. I plan to support a bunch of functionality within this input that is not just a plain search string (e.g. the ability to also use key=value or !shebang type entries). Due to this, directly exposing it to bubble-table won't work, as the Value() output won't be what I'm looking to filter with. The way my usecase works, is it sends a message to all components through the app, with the filter text when it changes, allowing each component to individually filter based off the specific usecases of each component.

What would be awesome to have supported, is a field that lets me specify the actual text for filtering, where "" is no filtering, and anything else would be used as filtering. With this, I would be able to support key-value type filtering, but also regular text filtering.

Alternatively, loosening the restrictions on the WithFilterInput() method, to utilize an interface of Value() string, and selectively interface checking for other features like Focus(), so it only calls those if the underlying type has those methods.

Thoughts?

Static footer cannot keep up with flex table.

I just started using the latest release, and tried out the new flex table. I had a custom static footer in my app, inspired by one of your examples. Turns out, the footer cannot keep up with the width of the flex table. I guess that, somewhere in the code, the width of the static footer is not being updated, causing the issue. This makes the entire thing look a bit awkward:

bug

Let me know if I missed something.

PS: Thanks for all the new features 😁 ! All the contributors are doing some great work here 👍🏾 .

Key bindings

Should use the KeyMap Bubble for key bindings so they can be more easily customized/visible to users.

when filter rows, all rows should re-paginated

When filter rows, all rows should re-paginated.

Current situation: pagination data is still all rows, so if there is no matched rows in first page, it will be empty. But in the second page, data will be shown.

Wanted: When filtering, rows is paginated by filtered data, and the pagination info will be combined with two part (current num, and total num).

feat: method to expand to fill height tea.WithAltScreen()

Hello -- awesome library. I'm looking to use this with tea.WithAltScreen(), however I'm struggling to figure out the best approach to have the table expand to fill the entire height. This is the current layout of my UI (ignore the broken background):

I'd essentially like the outer border to expand towards the bottom like shown below, however I don't see any height fields exposed to allow such a configuration.

My first two thoughts would be:

  1. An empty row, with no column dividers (and depending on if footer is enabled, the borders would be rounded).
  2. A flag for toggling outer borders, but still allowing inner borders. This could allow the user to draw their own border. Currently, toggling border settings seems to apply across the board, including all columns/rows.

Thoughts? Is there another way that I'm missing to do this? I've thought about repurposing the footer, and dynamically changing the height of it, but that feels fragile given the dynamic height of some of the other components inside of the table.

Hyperlinks break the table

I am trying to add hyperlinks to my bubble-table, but OSC 8 hyperlinks don't work when inside the table.

I am using this function to create the link:

func hyperlink(text string, url string) string {
	return "\x1b]8;;" + url + "\x07" + text + "\x1b]8;;\x07"
}

You can see an example code in this Gist. It renders like this:
Screenshot 2022-05-16 at 12 38 24

Same table, without hyperlinks:
Screenshot 2022-05-16 at 12 39 23

Add rounded border as built in option

Right now the thick square border is a very reasonable default, but building entirely new custom borders can be tough. It would be nice to have the rounded border as in the Pokémon example as a standard option available via a function call like WithRoundedBorder() or similar to give users more out of the box options.

Add state query functions / state change messages

Things I want to check outside the table:

  • Which column is the table sorted by?
  • Is a filter active?
  • What is the current filter string?

Possible tea.Msg:

  • filtering started
  • filtering changed
  • filtering ended
  • row changed
  • row selected
  • etc.

Table layout breaks when a value is multiline

If the value of one the columns includes a newline character (and the string before it is smaller than the column's max width) the table layout will break.

This can be reproduced by changing the value in line 28 of examples/simplest/main.go to Electric\nhello

This is because the rune width of the first line is smaller than the max width, I believe the expected behavior should be to only show the first line appended by the dots (to indicate it includes further data).

Support set the page size dynamic

Reason: In common TUI projects, Terminal height is allowed to change. This allows auto-adapting in a full-screen mode (e.g. top command).

Current Status: The page size can not be modified, and adjusting the pageSize during runtime cannot change the table height.

Expectation: Table can be adjust page size at runtime, or allow setting max height to table for calculate page size automatically.

String length limiting broken for double-width characters

The following string word-wraps incorrectly:

直立した恐竜のような身体と、尻尾の先端に常に燃えている炎が特徴。

This is likely because we're measuring rune width and not character width.

The fix should likely be in table/strlimit.go

Better borders

Borders are hardcoded for now. Should supply a way to customize them.

Handle Rows / Rowdata with different number of columns

I'm trying to create a table which will have data parsed from a CSV file.

CSVs are tricky beasts, and there may be cases where records may have different number of fields.

name,address,detail
This,Is a,good record
This,record,has,some,more,data
This is too,small

Now, I could make a pass over the file, find out the MAXIMUM number of columns and create a table with that many columns.

But is there some other approach we could take?

I don't want to make multiple passes over the data as I want to show the table as soon as possible (say with first 50 rows), while i load the rest of the CSV async.

Can we dynamically add columns? What would happen to all other rows that don't have that column's value?

Having a model that could represent arbitrarily sized rows would be great!

How to dynamically change header?

How can I show an indicator for the column used for sorting?

The column.title is not accessible, and there is no function to alter it.

artifacts on terminal resize

Hi, thanks for the great library!

I've noticed that when using flex columns if I resize my terminal then some table artifacts appear on the screen. Any idea on how to clear this out when the terminal window resizes?

image

After a screen resize:

image

Feature request: select rows programatically

Hi! It's me again 😸

I've been using this component for a project soon to be open sourced and it's been great so far 👏🏽 but I've found myself in the situation of missing how to select rows programmatically. It would be nice if there was a Row.Selected(bool) method or similar that would allow for marking the rows as selected before displaying them. Also methods on Model to select all/none could be very handy.

What do you think? I could try taking a stab at this in a PR.

Add contributing notes

There's been some increased contribution activity lately, thanks all! I'd like to add some extra notes about contributing which will hopefully be helpful.

Cell selection

We have row selection, but not cell selection. We should be able to move left/right to highlight a specific cell and make that information available to parent components in the same way as the row.

Examples to include screenshots/gifs

This is a request to include screenshots or gifs in the examples, just to get a better sense of not just the code required, but the presentation as well without requiring someone to run the example.

Reference table is ugly

While it's helpful to have a table that uses the full feature set, the reference table on the main page is pretty... ugly.

It would be helpful to have a nicely styled and visually appealing table to put on the readme, while keeping the feature demo intact as a code reference.

Despite enjoying the framework and building this repo, I'm pretty bad at making nice user interfaces, so I think this is a good opportunity for someone to make something reasonable that we can show off. This should be a new example in the examples directory, and potentially the default example in the Makefile.

Allow table to be horizontally scrollable with full-width columns

There should be a way to disallow columns being truncated like this, and instead allow the table to stretch horizontally offscreen, which would then be able to be scrolled left and right with the arrow keys. There's an example output below for a table with many columns where truncation makes the table pretty much useless.

171625774-93a43670-106f-468d-8d04-deaa46be7ca6

Go to Start/End Keys for Paginator

This is just a simple feature request: I think it will be nice if we have "Go to Start" and "Go to End" key bindings for the paginator, mapped to the Home and End keys. I think the implementation should be simple, but I was a bit unsure of how to add the functionality myself, so I did not go ahead with a PR. I thought this might be something on your mind as well, so I opened up an an issue instead.

PS: Great work on this project btw 😁 ! Thanks to this I did not have to go to all the trouble to try and come up with a table widget myself. The speed at which you are implementing all these features is almost unreal 😮 ! I started using it when you implemented pagination, which was a feature I sorely needed (the data I wanted to display had more than 3000 entries!).

Conflicting keymap entries cause confusing behavior

Currently, the keymap is checked one by one until a match is found, and then an action is taken. A KeyMap that starts with the default (as recommended) may end up with a conflicting key for an existing action that the developer is unaware of, and if one of those actions occurs earlier in the chain then this can 'eat' the action and be confusing.

Instead, all actions that match a keymap should be performed. This may cause some unintentional behavior while developing using the table, but this will highlight the conflict much more clearly and allow 'benign' conflicts (like page vs horizontal scrolling when there are no pages) to coexist.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.