Skip to content

Commit

Permalink
feat: add a Styler interface for styling bubbles
Browse files Browse the repository at this point in the history
This change introduces a Styler interface that allows users to define
custom styles for bubbles. The interface is implemented by the StylerFunc
type, which is a function type that takes a boolean argument and returns
a value of any type.

This allows a user to define a custom StylerFunc that returns a custom
style for a bubble based on the background color of the terminal.
  • Loading branch information
aymanbagabas committed Oct 29, 2024
1 parent db64e3c commit 8fdc81c
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 33 deletions.
14 changes: 14 additions & 0 deletions bubbles.go
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
package bubbles

// Styler represents an interface for styling bubbles.
type Styler[T any] interface {
Styles(isDark bool) T
}

// StylerFunc is a function type that implements the Styler interface.
type StylerFunc[T any] func(isDark bool) T

// Styles calls the function.
// It implements the Styler interface.
func (f StylerFunc[T]) Styles(isDark bool) T {
return f(isDark)
}
17 changes: 4 additions & 13 deletions help/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ type Styles struct {
FullSeparator lipgloss.Style
}

func newStyles(isDark bool) Styles {
// DefaultStyles returns a set of default styles for the help bubble.
func DefaultStyles(isDark bool) Styles {
lightDark := lipgloss.LightDark(isDark)

keyStyle := lipgloss.NewStyle().Foreground(lightDark("#909090", "#626262"))
Expand All @@ -59,16 +60,6 @@ func newStyles(isDark bool) Styles {
}
}

// DefaultDarkStyles returns a set of default styles for dark backgrounds.
func DefaultDarkStyles() Styles {
return newStyles(true)
}

// DefaultLightStyles returns a set of default styles for light backgrounds.
func DefaultLightStyles() Styles {
return newStyles(false)
}

// Model contains the state of the help view.
type Model struct {
Width int
Expand All @@ -85,12 +76,12 @@ type Model struct {
}

// New creates a new help view with some useful defaults.
func New() Model {
func New(isDark bool) Model {
return Model{
ShortSeparator: " • ",
FullSeparator: " ",
Ellipsis: "…",
Styles: DefaultDarkStyles(),
Styles: DefaultStyles(isDark),
}
}

Expand Down
2 changes: 1 addition & 1 deletion help/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

func TestFullHelp(t *testing.T) {
m := New()
m := New(true)
m.FullSeparator = " | "
k := key.WithKeys("x")
kb := [][]key.Binding{
Expand Down
10 changes: 4 additions & 6 deletions list/defaultitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,14 @@ type DefaultDelegate struct {
}

// NewDefaultDelegate creates a new delegate with default styles.
func NewDefaultDelegate() DefaultDelegate {
func NewDefaultDelegate(isDark bool) DefaultDelegate {
const defaultHeight = 2
const defaultSpacing = 1
return DefaultDelegate{
ShowDescription: true,
// XXX: Let the user choose between light and dark colors. We've
// temporarily hardcoded the dark colors here.
Styles: NewDefaultItemStyles(true),
height: defaultHeight,
spacing: defaultSpacing,
Styles: NewDefaultItemStyles(isDark),
height: defaultHeight,
spacing: defaultSpacing,
}
}

Expand Down
8 changes: 3 additions & 5 deletions list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,8 @@ type Model struct {
}

// New returns a new model with sensible defaults.
func New(items []Item, delegate ItemDelegate, width, height int) Model {
// XXX: Let the user choose between light and dark colors. We've
// temporarily hardcoded the dark colors here.
styles := DefaultStyles(true)
func New(items []Item, delegate ItemDelegate, width, height int, isDark bool) Model {
styles := DefaultStyles(isDark)

sp := spinner.New()
sp.Spinner = spinner.Line
Expand Down Expand Up @@ -239,7 +237,7 @@ func New(items []Item, delegate ItemDelegate, width, height int) Model {
items: items,
Paginator: p,
spinner: sp,
Help: help.New(),
Help: help.New(isDark),
}

m.updatePagination()
Expand Down
10 changes: 5 additions & 5 deletions list/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (d itemDelegate) Render(w io.Writer, m Model, index int, listItem Item) {
}

func TestStatusBarItemName(t *testing.T) {
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10)
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10, true)
expected := "2 items"
if !strings.Contains(list.statusView(), expected) {
t.Fatalf("Error: expected view to contain %s", expected)
Expand All @@ -44,7 +44,7 @@ func TestStatusBarItemName(t *testing.T) {
}

func TestStatusBarWithoutItems(t *testing.T) {
list := New([]Item{}, itemDelegate{}, 10, 10)
list := New([]Item{}, itemDelegate{}, 10, 10, true)

expected := "No items"
if !strings.Contains(list.statusView(), expected) {
Expand All @@ -53,7 +53,7 @@ func TestStatusBarWithoutItems(t *testing.T) {
}

func TestCustomStatusBarItemName(t *testing.T) {
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10)
list := New([]Item{item("foo"), item("bar")}, itemDelegate{}, 10, 10, true)
list.SetStatusBarItemName("connection", "connections")

expected := "2 connections"
Expand All @@ -77,7 +77,7 @@ func TestCustomStatusBarItemName(t *testing.T) {
func TestSetFilterText(t *testing.T) {
tc := []Item{item("foo"), item("bar"), item("baz")}

list := New(tc, itemDelegate{}, 10, 10)
list := New(tc, itemDelegate{}, 10, 10, true)
list.SetFilterText("ba")

list.SetFilterState(Unfiltered)
Expand All @@ -102,7 +102,7 @@ func TestSetFilterText(t *testing.T) {
func TestSetFilterState(t *testing.T) {
tc := []Item{item("foo"), item("bar"), item("baz")}

list := New(tc, itemDelegate{}, 10, 10)
list := New(tc, itemDelegate{}, 10, 10, true)
list.SetFilterText("ba")

list.SetFilterState(Unfiltered)
Expand Down
4 changes: 2 additions & 2 deletions textarea/textarea.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,12 @@ type Model struct {
}

// New creates a new model with default settings.
func New() Model {
func New(isDark bool) Model {
vp := viewport.New(0, 0)
vp.KeyMap = viewport.KeyMap{}
cur := cursor.New()

styles := DefaultStyles(true)
styles := DefaultStyles(isDark)

m := Model{
CharLimit: defaultCharLimit,
Expand Down
2 changes: 1 addition & 1 deletion textarea/textarea_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1703,7 +1703,7 @@ func TestView(t *testing.T) {
}

func newTextArea() Model {
textarea := New()
textarea := New(true)

textarea.Prompt = "> "
textarea.Placeholder = "Hello, World!"
Expand Down

0 comments on commit 8fdc81c

Please sign in to comment.