Skip to content

Commit

Permalink
Add row style func (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
Evertras authored May 18, 2024
1 parent 9f47e64 commit ef5b263
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 13 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ determining overrides. The default base style is a basic right-alignment.
[See the main feature example](./examples/features) to see styles and
how they override each other.

Styles can also be applied via a style function which can be used to apply
zebra striping, data-specific formatting, etc.

Can be focused to highlight a row and navigate with up/down (and j/k). These
keys can be customized with a KeyMap.

Expand Down
35 changes: 22 additions & 13 deletions examples/updates/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,30 @@ type Model struct {
data []*SomeData
}

func rowStyleFunc(input table.RowStyleFuncInput) lipgloss.Style {
calculatedStyle := lipgloss.NewStyle()

switch input.Row.Data[columnKeyStatus] {
case "Critical":
calculatedStyle = styleCritical.Copy()
case "Stable":
calculatedStyle = styleStable.Copy()
case "Good":
calculatedStyle = styleGood.Copy()
}

if input.Index%2 == 0 {
calculatedStyle = calculatedStyle.Background(lipgloss.Color("#222"))
} else {
calculatedStyle = calculatedStyle.Background(lipgloss.Color("#444"))
}

return calculatedStyle
}

func NewModel() Model {
return Model{
table: table.New(generateColumns(0)),
table: table.New(generateColumns(0)).WithRowStyleFunc(rowStyleFunc),
updateDelay: time.Second,
}
}
Expand Down Expand Up @@ -152,18 +173,6 @@ func generateRowsFromData(data []*SomeData) []table.Row {
columnKeyStatus: entry.Status,
})

// Highlight different statuses
switch entry.Status {
case "Critical":
row = row.WithStyle(styleCritical)

case "Stable":
row = row.WithStyle(styleStable)

case "Good":
row = row.WithStyle(styleGood)
}

rows = append(rows, row)
}

Expand Down
1 change: 1 addition & 0 deletions table/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Model struct {
baseStyle lipgloss.Style
highlightStyle lipgloss.Style
headerStyle lipgloss.Style
rowStyleFunc func(RowStyleFuncInput) lipgloss.Style
border Border
selectedText string
unselectedText string
Expand Down
24 changes: 24 additions & 0 deletions table/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ import (
"github.com/charmbracelet/lipgloss"
)

// RowStyleFuncInput is the input to the style function that can
// be applied to each row. This is useful for things like zebra
// striping or other data-based styles.
//
// Note that we use a struct here to allow for future expansion
// while keeping backwards compatibility.
type RowStyleFuncInput struct {
// Index is the index of the row, starting at 0.
Index int

// Row is the full row data.
Row Row
}

// WithRowStyleFunc sets a function that can be used to apply a style to each row
// based on the row data. This is useful for things like zebra striping or other
// data-based styles. It can be safely set to nil to remove it later.
// This style is applied after the base style and before individual row styles.
func (m Model) WithRowStyleFunc(f func(RowStyleFuncInput) lipgloss.Style) Model {
m.rowStyleFunc = f

return m
}

// WithHighlightedRow sets the highlighted row to the given index.
func (m Model) WithHighlightedRow(index int) Model {
m.rowCursorIndex = index
Expand Down
9 changes: 9 additions & 0 deletions table/row.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ func (m Model) renderRow(rowIndex int, last bool) string {

rowStyle := row.Style.Copy()

if m.rowStyleFunc != nil {
styleResult := m.rowStyleFunc(RowStyleFuncInput{
Index: rowIndex,
Row: row,
})

rowStyle = rowStyle.Inherit(styleResult)
}

if m.focused && highlighted {
rowStyle = rowStyle.Inherit(m.highlightStyle)
}
Expand Down
51 changes: 51 additions & 0 deletions table/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,57 @@ func TestSimple3x3StyleOverridesAsBaseColumnRowCell(t *testing.T) {
assert.Equal(t, expectedTable, rendered)
}

func TestStyleFuncAppliesAfterBaseStyleAndColStylesAndBeforeRowStyle(t *testing.T) {
styleFunc := func(input RowStyleFuncInput) lipgloss.Style {
if input.Index%2 == 0 {
return lipgloss.NewStyle().Align(lipgloss.Left)
}

return lipgloss.NewStyle()
}

model := New([]Column{
NewColumn("1", "1", 6),
// This column style should be overridden by the style func
NewColumn("2", "2", 6).WithStyle(lipgloss.NewStyle().Align(lipgloss.Right)),
NewColumn("3", "3", 6),
}).
WithBaseStyle(lipgloss.NewStyle().Align(lipgloss.Center)).
WithRowStyleFunc(styleFunc)

rows := []Row{}

for rowIndex := 1; rowIndex <= 5; rowIndex++ {
rowData := RowData{}

for columnIndex := 1; columnIndex <= 3; columnIndex++ {
id := fmt.Sprintf("%d", columnIndex)

rowData[id] = fmt.Sprintf("%d,%d", columnIndex, rowIndex)
}

rows = append(rows, NewRow(rowData))
}

rows[0] = rows[0].WithStyle(lipgloss.NewStyle().Align(lipgloss.Right))

model = model.WithRows(rows)

const expectedTable = `┏━━━━━━┳━━━━━━┳━━━━━━┓
┃ 1 ┃ 2┃ 3 ┃
┣━━━━━━╋━━━━━━╋━━━━━━┫
┃ 1,1┃ 2,1┃ 3,1┃
┃ 1,2 ┃ 2,2┃ 3,2 ┃
┃1,3 ┃2,3 ┃3,3 ┃
┃ 1,4 ┃ 2,4┃ 3,4 ┃
┃1,5 ┃2,5 ┃3,5 ┃
┗━━━━━━┻━━━━━━┻━━━━━━┛`

rendered := model.View()

assert.Equal(t, expectedTable, rendered)
}

// This is a long test due to typing and multiple big table strings, that's okay
//
//nolint:funlen
Expand Down

0 comments on commit ef5b263

Please sign in to comment.