Skip to content

Commit 495d893

Browse files
authored
Add cell style func (#203)
* Add cell style func * Ignore the lint on funlen for now * Proper nolint, whoops
1 parent 1dbd3e0 commit 495d893

File tree

4 files changed

+120
-15
lines changed

4 files changed

+120
-15
lines changed

examples/pokemon/main.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,24 @@ type Model struct {
3535
pokeTable table.Model
3636
}
3737

38-
func makeRow(name, element, colorStr string, numConversations int, positiveSentiment, negativeSentiment float32) table.Row {
38+
func makeRow(name, element string, numConversations int, positiveSentiment, negativeSentiment float32) table.Row {
39+
elementStyleFunc := func(input table.StyledCellFuncInput) lipgloss.Style {
40+
switch input.Data.(string) {
41+
case "Fire":
42+
return lipgloss.NewStyle().Foreground(lipgloss.Color(colorFire))
43+
case "Water":
44+
return lipgloss.NewStyle().Foreground(lipgloss.Color(colorWater))
45+
case "Plant":
46+
return lipgloss.NewStyle().Foreground(lipgloss.Color(colorPlant))
47+
case "Electric":
48+
return lipgloss.NewStyle().Foreground(lipgloss.Color(colorElectric))
49+
default:
50+
return lipgloss.NewStyle().Foreground(lipgloss.Color(colorNormal))
51+
}
52+
}
3953
return table.NewRow(table.RowData{
4054
columnKeyName: name,
41-
columnKeyElement: table.NewStyledCell(element, lipgloss.NewStyle().Foreground(lipgloss.Color(colorStr))),
55+
columnKeyElement: table.NewStyledCellWithStyleFunc(element, elementStyleFunc),
4256
columnKeyConversations: numConversations,
4357
columnKeyPositiveSentiment: positiveSentiment,
4458
columnKeyNegativeSentiment: negativeSentiment,
@@ -58,13 +72,13 @@ func NewModel() Model {
5872
WithStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("#c88"))).
5973
WithFormatString("%.1f%%"),
6074
}).WithRows([]table.Row{
61-
makeRow("Pikachu", "Electric", colorElectric, 2300648, 21.9, 8.54),
62-
makeRow("Eevee", "Normal", colorNormal, 636373, 26.4, 7.37),
63-
makeRow("Bulbasaur", "Plant", colorPlant, 352190, 25.7, 9.02),
64-
makeRow("Squirtle", "Water", colorWater, 241259, 25.6, 5.96),
65-
makeRow("Blastoise", "Water", colorWater, 162794, 19.5, 6.04),
66-
makeRow("Charmander", "Fire", colorFire, 265760, 31.2, 5.25),
67-
makeRow("Charizard", "Fire", colorFire, 567763, 25.6, 7.56),
75+
makeRow("Pikachu", "Electric", 2300648, 21.9, 8.54),
76+
makeRow("Eevee", "Normal", 636373, 26.4, 7.37),
77+
makeRow("Bulbasaur", "Plant", 352190, 25.7, 9.02),
78+
makeRow("Squirtle", "Water", 241259, 25.6, 5.96),
79+
makeRow("Blastoise", "Water", 162794, 19.5, 6.04),
80+
makeRow("Charmander", "Fire", 265760, 31.2, 5.25),
81+
makeRow("Charizard", "Fire", 567763, 25.6, 7.56),
6882
}).
6983
BorderRounded().
7084
WithBaseStyle(styleBase).

table/cell.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,47 @@ import "github.com/charmbracelet/lipgloss"
88
// limited to colors, font style, and alignments - spacing style such as margin
99
// will break the table format.
1010
type StyledCell struct {
11-
Data any
11+
// Data is the content of the cell.
12+
Data any
13+
14+
// Style is the specific style to apply. This is ignored if StyleFunc is not nil.
1215
Style lipgloss.Style
16+
17+
// StyleFunc is a function that takes the row/column of the cell and
18+
// returns a lipgloss.Style allowing for dynamic styling based on the cell's
19+
// content or position. Overrides Style if set.
20+
StyleFunc StyledCellFunc
21+
}
22+
23+
// StyledCellFuncInput is the input to the StyledCellFunc. Sent as a struct
24+
// to allow for future additions without breaking changes.
25+
type StyledCellFuncInput struct {
26+
// Data is the data in the cell.
27+
Data any
28+
29+
// Column is the column that the cell belongs to.
30+
Column Column
1331
}
1432

33+
// StyledCellFunc is a function that takes various information about the cell and
34+
// returns a lipgloss.Style allowing for easier dynamic styling based on the cell's
35+
// content or position.
36+
type StyledCellFunc = func(input StyledCellFuncInput) lipgloss.Style
37+
1538
// NewStyledCell creates an entry that can be set in the row data and show as
1639
// styled with the given style.
1740
func NewStyledCell(data any, style lipgloss.Style) StyledCell {
18-
return StyledCell{data, style}
41+
return StyledCell{
42+
Data: data,
43+
Style: style,
44+
}
45+
}
46+
47+
// NewStyledCellWithStyleFunc creates an entry that can be set in the row data and show as
48+
// styled with the given style function.
49+
func NewStyledCellWithStyleFunc(data any, styleFunc StyledCellFunc) StyledCell {
50+
return StyledCell{
51+
Data: data,
52+
StyleFunc: styleFunc,
53+
}
1954
}

table/row.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (r Row) WithStyle(style lipgloss.Style) Row {
5353
return r
5454
}
5555

56-
//nolint:cyclop // This has many ifs, but they're short
56+
//nolint:cyclop,funlen // Breaking this up will be more complicated than it's worth for now
5757
func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Style, borderStyle lipgloss.Style) string {
5858
cellStyle := rowStyle.Copy().Inherit(column.style).Inherit(m.baseStyle)
5959

@@ -91,7 +91,15 @@ func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Sty
9191
switch entry := data.(type) {
9292
case StyledCell:
9393
str = fmt.Sprintf(fmtString, entry.Data)
94-
cellStyle = entry.Style.Copy().Inherit(cellStyle)
94+
95+
if entry.StyleFunc != nil {
96+
cellStyle = entry.StyleFunc(StyledCellFuncInput{
97+
Column: column,
98+
Data: entry.Data,
99+
}).Copy().Inherit(cellStyle)
100+
} else {
101+
cellStyle = entry.Style.Copy().Inherit(cellStyle)
102+
}
95103
default:
96104
str = fmt.Sprintf(fmtString, entry)
97105
}

table/view_test.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,55 @@ func TestSimple3x3StyleOverridesAsBaseColumnRowCell(t *testing.T) {
798798
assert.Equal(t, expectedTable, rendered)
799799
}
800800

801-
func TestStyleFuncAppliesAfterBaseStyleAndColStylesAndBeforeRowStyle(t *testing.T) {
801+
func TestSimple3x3CellStyleFuncOverridesAsBaseColumnRowCell(t *testing.T) {
802+
model := New([]Column{
803+
NewColumn("1", "1", 6),
804+
NewColumn("2", "2", 6).WithStyle(lipgloss.NewStyle().Align(lipgloss.Left)),
805+
NewColumn("3", "3", 6),
806+
}).WithBaseStyle(lipgloss.NewStyle().Align(lipgloss.Center))
807+
808+
rows := []Row{}
809+
810+
for rowIndex := 1; rowIndex <= 3; rowIndex++ {
811+
rowData := RowData{}
812+
813+
for columnIndex := 1; columnIndex <= 3; columnIndex++ {
814+
id := fmt.Sprintf("%d", columnIndex)
815+
816+
rowData[id] = fmt.Sprintf("%d,%d", columnIndex, rowIndex)
817+
}
818+
819+
rows = append(rows, NewRow(rowData))
820+
}
821+
822+
// Test overrides with alignment because it's easy to check output string
823+
rows[0] = rows[0].WithStyle(lipgloss.NewStyle().Align(lipgloss.Left))
824+
rows[0].Data["2"] = NewStyledCellWithStyleFunc("R", func(input StyledCellFuncInput) lipgloss.Style {
825+
// Do some checks to make sure we're given the right information as a bonus test
826+
assert.Equal(t, "2", input.Column.Key(), "Wrong column key given to style func")
827+
assert.Equal(t, "R", input.Data, "Wrong data given to style func")
828+
829+
return lipgloss.NewStyle().Align(lipgloss.Right)
830+
})
831+
832+
rows[2] = rows[2].WithStyle(lipgloss.NewStyle().Align(lipgloss.Right))
833+
834+
model = model.WithRows(rows)
835+
836+
const expectedTable = `┏━━━━━━┳━━━━━━┳━━━━━━┓
837+
┃ 1 ┃2 ┃ 3 ┃
838+
┣━━━━━━╋━━━━━━╋━━━━━━┫
839+
┃1,1 ┃ R┃3,1 ┃
840+
┃ 1,2 ┃2,2 ┃ 3,2 ┃
841+
┃ 1,3┃ 2,3┃ 3,3┃
842+
┗━━━━━━┻━━━━━━┻━━━━━━┛`
843+
844+
rendered := model.View()
845+
846+
assert.Equal(t, expectedTable, rendered)
847+
}
848+
849+
func TestRowStyleFuncAppliesAfterBaseStyleAndColStylesAndBeforeRowStyle(t *testing.T) {
802850
styleFunc := func(input RowStyleFuncInput) lipgloss.Style {
803851
if input.Index%2 == 0 {
804852
return lipgloss.NewStyle().Align(lipgloss.Left)
@@ -849,7 +897,7 @@ func TestStyleFuncAppliesAfterBaseStyleAndColStylesAndBeforeRowStyle(t *testing.
849897
assert.Equal(t, expectedTable, rendered)
850898
}
851899

852-
func TestStyleFuncAppliesHighlighted(t *testing.T) {
900+
func TestRowStyleFuncAppliesHighlighted(t *testing.T) {
853901
styleFunc := func(input RowStyleFuncInput) lipgloss.Style {
854902
if input.IsHighlighted {
855903
return lipgloss.NewStyle().Align(lipgloss.Center)

0 commit comments

Comments
 (0)