diff --git a/go.mod b/go.mod index c64b7f8..57141d9 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/charmbracelet/harmonica v0.2.0 github.com/charmbracelet/lipgloss v0.12.1 github.com/charmbracelet/x/ansi v0.1.4 + github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b github.com/dustin/go-humanize v1.0.1 github.com/lucasb-eyer/go-colorful v1.2.0 github.com/mattn/go-runewidth v0.0.16 @@ -19,6 +20,7 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymanbagabas/go-udiff v0.2.0 // indirect github.com/charmbracelet/x/input v0.1.0 // indirect github.com/charmbracelet/x/term v0.1.1 // indirect github.com/charmbracelet/x/windows v0.1.0 // indirect diff --git a/go.sum b/go.sum index 2273bef..a6bd9e9 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/charmbracelet/bubbletea v0.27.0 h1:Mznj+vvYuYagD9Pn2mY7fuelGvP0HAXtZYGgRBCbHvU= github.com/charmbracelet/bubbletea v0.27.0/go.mod h1:5MdP9XH6MbQkgGhnlxUqCNmBXf9I74KRQ8HIidRxV1Y= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= @@ -12,6 +14,8 @@ github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/N github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= diff --git a/table/table.go b/table/table.go index 3209eb7..41875b0 100644 --- a/table/table.go +++ b/table/table.go @@ -14,12 +14,11 @@ import ( type Model struct { KeyMap KeyMap - cols []Column - rows []Row - cursor int - focus bool - styles Styles - styleFunc StyleFunc + cols []Column + rows []Row + cursor int + focus bool + styles Styles viewport viewport.Model start int @@ -189,13 +188,6 @@ func WithStyles(s Styles) Option { } } -// WithStyleFunc sets the table style func which can determine a cell style per column, row, and selected state. -func WithStyleFunc(f StyleFunc) Option { - return func(m *Model) { - m.styleFunc = f - } -} - // WithKeyMap sets the key map. func WithKeyMap(km KeyMap) Option { return func(m *Model) { @@ -405,9 +397,6 @@ func (m *Model) FromValues(value, separator string) { m.SetRows(rows) } -// StyleFunc is a function that can be used to customize the style of a table cell based on the row and column index. -type StyleFunc func(row, col int, value string) lipgloss.Style - func (m Model) headersView() string { s := make([]string, 0, len(m.cols)) for _, col := range m.cols { @@ -427,18 +416,8 @@ func (m *Model) renderRow(r int) string { if m.cols[i].Width <= 0 { continue } - var cellStyle lipgloss.Style - if m.styleFunc != nil { - cellStyle = m.styleFunc(r, i, value) - if r == m.cursor { - cellStyle = cellStyle.Inherit(m.styles.Selected) - } - } else { - cellStyle = m.styles.Cell - } - style := lipgloss.NewStyle().Width(m.cols[i].Width).MaxWidth(m.cols[i].Width).Inline(true) - renderedCell := cellStyle.Render(style.Render(runewidth.Truncate(value, m.cols[i].Width, "…"))) + renderedCell := m.styles.Cell.Render(style.Render(runewidth.Truncate(value, m.cols[i].Width, "…"))) s = append(s, renderedCell) } diff --git a/table/table_test.go b/table/table_test.go index 24f075f..cc49f0d 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -1,10 +1,11 @@ package table import ( - "strings" "testing" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/exp/golden" ) func TestFromValues(t *testing.T) { @@ -58,13 +59,11 @@ func deepEqual(a, b []Row) bool { return true } -var ( - cols = []Column{ - {Title: "col1", Width: 10}, - {Title: "col2", Width: 10}, - {Title: "col3", Width: 10}, - } -) +var cols = []Column{ + {Title: "col1", Width: 10}, + {Title: "col2", Width: 10}, + {Title: "col3", Width: 10}, +} func TestRenderRow(t *testing.T) { tests := []struct { @@ -109,33 +108,52 @@ func TestRenderRow(t *testing.T) { }) } } -func TestRenderRowStyleFunc(t *testing.T) { - tests := []struct { - name string - table *Model - expected string - }{ - { - name: "simple row", - table: &Model{ - rows: []Row{{"Foooooo", "Baaaaar", "Baaaaaz"}}, - cols: cols, - styleFunc: func(row, col int, value string) lipgloss.Style { - if strings.HasSuffix(value, "z") { - return lipgloss.NewStyle().Transform(strings.ToLower) - } - return lipgloss.NewStyle().Transform(strings.ToUpper) - }, - }, - expected: "FOOOOOO BAAAAAR baaaaaz ", - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - row := tc.table.renderRow(0) - if row != tc.expected { - t.Fatalf("\n\nWant: \n%s\n\nGot: \n%s\n", tc.expected, row) - } - }) - } + +func TestTableAlignment(t *testing.T) { + t.Run("No border", func(t *testing.T) { + biscuits := New( + WithHeight(5), + WithColumns([]Column{ + {Title: "Name", Width: 25}, + {Title: "Country of Origin", Width: 16}, + {Title: "Dunk-able", Width: 12}, + }), + WithRows([]Row{ + {"Chocolate Digestives", "UK", "Yes"}, + {"Tim Tams", "Australia", "No"}, + {"Hobnobs", "UK", "Yes"}, + }), + ) + got := ansi.Strip(biscuits.View()) + golden.RequireEqual(t, []byte(got)) + }) + t.Run("With border", func(t *testing.T) { + baseStyle := lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")) + + s := DefaultStyles() + s.Header = s.Header. + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")). + BorderBottom(true). + Bold(false) + + biscuits := New( + WithHeight(5), + WithColumns([]Column{ + {Title: "Name", Width: 25}, + {Title: "Country of Origin", Width: 16}, + {Title: "Dunk-able", Width: 12}, + }), + WithRows([]Row{ + {"Chocolate Digestives", "UK", "Yes"}, + {"Tim Tams", "Australia", "No"}, + {"Hobnobs", "UK", "Yes"}, + }), + WithStyles(s), + ) + got := ansi.Strip(baseStyle.Render(biscuits.View())) + golden.RequireEqual(t, []byte(got)) + }) } diff --git a/table/testdata/TestTableAlignment/No_border.golden b/table/testdata/TestTableAlignment/No_border.golden new file mode 100644 index 0000000..a4664a8 --- /dev/null +++ b/table/testdata/TestTableAlignment/No_border.golden @@ -0,0 +1,5 @@ + Name Country of Orig… Dunk-able + Chocolate Digestives UK Yes + Tim Tams Australia No + Hobnobs UK Yes + \ No newline at end of file diff --git a/table/testdata/TestTableAlignment/With_border.golden b/table/testdata/TestTableAlignment/With_border.golden new file mode 100644 index 0000000..49f7909 --- /dev/null +++ b/table/testdata/TestTableAlignment/With_border.golden @@ -0,0 +1,8 @@ +┌───────────────────────────────────────────────────────────┐ +│ Name Country of Orig… Dunk-able │ +│───────────────────────────────────────────────────────────│ +│ Chocolate Digestives UK Yes │ +│ Tim Tams Australia No │ +│ Hobnobs UK Yes │ +│ │ +└───────────────────────────────────────────────────────────┘ \ No newline at end of file