Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable filter works with table api(not in the footer) #73

Merged
merged 5 commits into from
May 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions examples/filterapi/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package main

import (
"github.com/charmbracelet/bubbles/textinput"
"log"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/evertras/bubble-table/table"
)

const (
columnKeyTitle = "title"
columnKeyAuthor = "author"
columnKeyDescription = "description"
)

type Model struct {
table table.Model
filterTextInput textinput.Model
}

func NewModel() Model {
columns := []table.Column{
table.NewColumn(columnKeyTitle, "Title", 13).WithFiltered(true),
table.NewColumn(columnKeyAuthor, "Author", 13).WithFiltered(true),
table.NewColumn(columnKeyDescription, "Description", 50),
}
return Model{
table: table.
New(columns).
Filtered(true).
Focused(true).
WithFooterVisibility(false).
WithPageSize(10).
WithRows([]table.Row{
table.NewRow(table.RowData{
columnKeyTitle: "Computer Systems : A Programmer's Perspective",
columnKeyAuthor: "Randal E. Bryant、David R. O'Hallaron / Prentice Hall ",
columnKeyDescription: "This book explains the important and enduring concepts underlying all computer...",
}),
table.NewRow(table.RowData{
columnKeyTitle: "Effective Java : 3rd Edition",
columnKeyAuthor: "Joshua Bloch",
columnKeyDescription: "The Definitive Guide to Java Platform Best Practices—Updated for Java 9 Java ...",
}),
table.NewRow(table.RowData{
columnKeyTitle: "Structure and Interpretation of Computer Programs - 2nd Edition (MIT)",
columnKeyAuthor: "Harold Abelson、Gerald Jay Sussman",
columnKeyDescription: "Structure and Interpretation of Computer Programs has had a dramatic impact on...",
}),
table.NewRow(table.RowData{
columnKeyTitle: "Game Programming Patterns",
columnKeyAuthor: "Robert Nystrom / Genever Benning",
columnKeyDescription: "The biggest challenge facing many game programmers is completing their game. M...",
}),
}),
filterTextInput: textinput.New(),
}
}

func (m Model) Init() tea.Cmd {
return nil
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)

switch msg := msg.(type) {
case tea.KeyMsg:
// global
if msg.String() == "ctrl+c" {
cmds = append(cmds, tea.Quit)
return m, tea.Batch(cmds...)
}
// event to filter
if m.filterTextInput.Focused() {
if msg.String() == "enter" {
m.filterTextInput.Blur()
} else {
m.filterTextInput, cmd = m.filterTextInput.Update(msg)
}
m.table = m.table.WithFilterInput(m.filterTextInput)
return m, tea.Batch(cmds...)
}

// others component
switch msg.String() {
case "/":
m.filterTextInput.Focus()
case "q":
cmds = append(cmds, tea.Quit)
return m, tea.Batch(cmds...)
default:
m.table, cmd = m.table.Update(msg)
cmds = append(cmds, cmd)
}

}

return m, tea.Batch(cmds...)
}

func (m Model) View() string {
body := strings.Builder{}

body.WriteString("A filtered simple default table\n" +
"Currently filter by Title and Author, press / + letters to start filtering, and escape to clear filter.\nPress q or ctrl+c to quit\n\n")

body.WriteString(m.filterTextInput.View() + "\n")
body.WriteString(m.table.View())

return body.String()
}

func main() {
p := tea.NewProgram(NewModel())

if err := p.Start(); err != nil {
log.Fatal(err)
}
}
25 changes: 25 additions & 0 deletions table/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"
"time"

"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -179,3 +180,27 @@ func TestGetFilteredRowsRefocusAfterFilter(t *testing.T) {
assert.Equal(t, 1, model.MaxPages())
assert.Equal(t, 0, model.TotalRows())
}

func TestFilterWithExternalTextInput(t *testing.T) {
columns := []Column{NewColumn("title", "title", 10).WithFiltered(true)}
rows := []Row{
NewRow(RowData{
"title": "AAA",
"description": "",
}),
NewRow(RowData{
"title": "BBB",
"description": "",
}),
// Empty
NewRow(RowData{}),
}
model := New(columns).WithRows(rows).Filtered(true)
input := textinput.New()
input.SetValue("AaA")
model = model.WithFilterInput(input)

filteredRows := model.getFilteredRows(rows)

assert.Len(t, filteredRows, 1)
}
4 changes: 2 additions & 2 deletions table/footer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

func (m Model) hasFooter() bool {
return m.staticFooter != "" || m.pageSize != 0 || m.filtered
return m.footerVisible && (m.staticFooter != "" || m.pageSize != 0 || m.filtered)
}

func (m Model) renderFooter() string {
Expand All @@ -25,7 +25,7 @@ func (m Model) renderFooter() string {
sections := []string{}

if m.filtered && (m.filterTextInput.Focused() || m.filterTextInput.Value() != "") {
sections = append(sections, fmt.Sprintf("/%s", m.filterTextInput.View()))
sections = append(sections, m.filterTextInput.View())
}

// paged feature enabled
Expand Down
6 changes: 4 additions & 2 deletions table/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ type Model struct {
unselectedText string

// Footers
staticFooter string
footerVisible bool
staticFooter string

// Pagination
pageSize int
Expand All @@ -60,11 +61,12 @@ type Model struct {
// New creates a new table ready for further modifications.
func New(columns []Column) Model {
filterInput := textinput.New()
filterInput.Prompt = ""
filterInput.Prompt = "/"
model := Model{
columns: make([]Column, len(columns)),
highlightStyle: defaultHighlightStyle.Copy(),
border: borderDefault,
footerVisible: true,
keyMap: DefaultKeyMap(),

selectedText: "[x]",
Expand Down
17 changes: 17 additions & 0 deletions table/options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package table

import (
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/lipgloss"
)

Expand Down Expand Up @@ -249,3 +250,19 @@ func (m Model) WithColumns(columns []Column) Model {

return m
}

// WithFilterInput makes the table use the provided text input bubble for
// filtering rather than using the built-in default. This allows for external
// text input controls to be used.
func (m Model) WithFilterInput(input textinput.Model) Model {
m.filterTextInput = input

return m
}

// WithFooterVisibility sets the visibility of the footer.
func (m Model) WithFooterVisibility(visibility bool) Model {
m.footerVisible = visibility

return m
}
7 changes: 7 additions & 0 deletions table/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,11 @@ func TestPageOptions(t *testing.T) {
model = model.WithCurrentPage(6)
assert.Equal(t, 6, model.CurrentPage())
assert.Equal(t, 26, model.rowCursorIndex)

model = model.WithFooterVisibility(false)
assert.Equal(t, "", model.renderFooter())

model = model.WithFooterVisibility(true)
assert.Greater(t, len(model.renderFooter()), 10)
assert.Contains(t, model.renderFooter(), "6/6")
}