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

feat: add option to set a custom lipgloss renderer #403

Closed
wants to merge 1 commit into from
Closed
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
28 changes: 26 additions & 2 deletions cursor/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,23 @@ type Model struct {
blinkTag int
// mode determines the behavior of the cursor
mode Mode

re *lipgloss.Renderer
}

// Option is used to set options in New.
type Option func(*Model)

// WithRenderer sets the Lip Gloss renderer for the cursor.
func WithRenderer(r *lipgloss.Renderer) Option {
return func(m *Model) {
m.re = r
}
}

// New creates a new model with default settings.
func New() Model {
return Model{
func New(opts ...Option) Model {
m := Model{
BlinkSpeed: defaultBlinkSpeed,

Blink: true,
Expand All @@ -86,6 +98,18 @@ func New() Model {
ctx: context.Background(),
},
}

for _, opt := range opts {
opt(&m)
}

if m.re == nil {
m.re = lipgloss.DefaultRenderer()
}

m.Style = m.Style.Renderer(m.re)

return m
}

// Update updates the cursor.
Expand Down
74 changes: 57 additions & 17 deletions filepicker/filepicker.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,19 @@ func nextID() int {
return lastID
}

// Option is used to set options in New.
type Option func(*Model)

// WithRenderer sets the Lip Gloss renderer for the filepicker.
func WithRenderer(r *lipgloss.Renderer) Option {
return func(m *Model) {
m.re = r
}
}

// New returns a new filepicker model with default styling and key bindings.
func New() Model {
return Model{
func New(opts ...Option) Model {
m := Model{
id: nextID(),
CurrentDirectory: ".",
Cursor: ">",
Expand All @@ -46,8 +56,19 @@ func New() Model {
minStack: newStack(),
maxStack: newStack(),
KeyMap: DefaultKeyMap(),
Styles: DefaultStyles(),
}

for _, opt := range opts {
opt(&m)
}

if m.re == nil {
m.re = lipgloss.DefaultRenderer()
}

m.Styles = DefaultStyles().Renderer(m.re)

return m
}

type errorMsg struct {
Expand Down Expand Up @@ -108,32 +129,51 @@ type Styles struct {
EmptyDirectory lipgloss.Style
}

// Renderer returns a new Styles copy with the given Lip Gloss renderer.
func (s Styles) Renderer(re *lipgloss.Renderer) Styles {
s.DisabledCursor = s.DisabledCursor.Copy().Renderer(re)
s.Cursor = s.Cursor.Copy().Renderer(re)
s.Symlink = s.Symlink.Copy().Renderer(re)
s.Directory = s.Directory.Copy().Renderer(re)
s.File = s.File.Copy().Renderer(re)
s.DisabledFile = s.DisabledFile.Copy().Renderer(re)
s.Permission = s.Permission.Copy().Renderer(re)
s.Selected = s.Selected.Copy().Renderer(re)
s.DisabledSelected = s.DisabledSelected.Copy().Renderer(re)
s.FileSize = s.FileSize.Copy().Renderer(re)
s.EmptyDirectory = s.EmptyDirectory.Copy().Renderer(re)
return s
}

// DefaultStyles defines the default styling for the file picker.
func DefaultStyles() Styles {
return DefaultStylesWithRenderer(lipgloss.DefaultRenderer())
return Styles{
DisabledCursor: lipgloss.NewStyle().Foreground(lipgloss.Color("247")),
Cursor: lipgloss.NewStyle().Foreground(lipgloss.Color("212")),
Symlink: lipgloss.NewStyle().Foreground(lipgloss.Color("36")),
Directory: lipgloss.NewStyle().Foreground(lipgloss.Color("99")),
File: lipgloss.NewStyle(),
DisabledFile: lipgloss.NewStyle().Foreground(lipgloss.Color("243")),
DisabledSelected: lipgloss.NewStyle().Foreground(lipgloss.Color("247")),
Permission: lipgloss.NewStyle().Foreground(lipgloss.Color("244")),
Selected: lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Bold(true),
FileSize: lipgloss.NewStyle().Foreground(lipgloss.Color("240")).Width(fileSizeWidth).Align(lipgloss.Right),
EmptyDirectory: lipgloss.NewStyle().Foreground(lipgloss.Color("240")).PaddingLeft(paddingLeft).SetString("Bummer. No Files Found."),
}
}

// DefaultStylesWithRenderer defines the default styling for the file picker,
// with a given Lip Gloss renderer.
//
// Deprecated: use Styles.Renderer instead.
func DefaultStylesWithRenderer(r *lipgloss.Renderer) Styles {
return Styles{
DisabledCursor: r.NewStyle().Foreground(lipgloss.Color("247")),
Cursor: r.NewStyle().Foreground(lipgloss.Color("212")),
Symlink: r.NewStyle().Foreground(lipgloss.Color("36")),
Directory: r.NewStyle().Foreground(lipgloss.Color("99")),
File: r.NewStyle(),
DisabledFile: r.NewStyle().Foreground(lipgloss.Color("243")),
DisabledSelected: r.NewStyle().Foreground(lipgloss.Color("247")),
Permission: r.NewStyle().Foreground(lipgloss.Color("244")),
Selected: r.NewStyle().Foreground(lipgloss.Color("212")).Bold(true),
FileSize: r.NewStyle().Foreground(lipgloss.Color("240")).Width(fileSizeWidth).Align(lipgloss.Right),
EmptyDirectory: r.NewStyle().Foreground(lipgloss.Color("240")).PaddingLeft(paddingLeft).SetString("Bummer. No Files Found."),
}
return DefaultStyles().Renderer(r)
}

// Model represents a file picker.
type Model struct {
id int
re *lipgloss.Renderer

// Path is the path which the user has selected with the file picker.
Path string
Expand Down
87 changes: 62 additions & 25 deletions help/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,43 @@ type Styles struct {
FullSeparator lipgloss.Style
}

// DefaultStyles returns a set of default styles for the help bubble.
func DefaultStyles() Styles {
keyStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#909090",
Dark: "#626262",
})
descStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#B2B2B2",
Dark: "#4A4A4A",
})
sepStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#DDDADA",
Dark: "#3C3C3C",
})
return Styles{
ShortKey: keyStyle,
ShortDesc: descStyle,
ShortSeparator: sepStyle,
Ellipsis: sepStyle.Copy(),
FullKey: keyStyle.Copy(),
FullDesc: descStyle.Copy(),
FullSeparator: sepStyle.Copy(),
}
}

// Renderer returns a copy of Styles with the given Lip Gloss renderer set.
func (s Styles) Renderer(r *lipgloss.Renderer) Styles {
s.Ellipsis = s.Ellipsis.Copy().Renderer(r)
s.ShortKey = s.ShortKey.Copy().Renderer(r)
s.ShortDesc = s.ShortDesc.Copy().Renderer(r)
s.ShortSeparator = s.ShortSeparator.Copy().Renderer(r)
s.FullKey = s.FullKey.Copy().Renderer(r)
s.FullDesc = s.FullDesc.Copy().Renderer(r)
s.FullSeparator = s.FullSeparator.Copy().Renderer(r)
return s
}

// Model contains the state of the help view.
type Model struct {
Width int
Expand All @@ -55,39 +92,39 @@ type Model struct {
Ellipsis string

Styles Styles
}

// New creates a new help view with some useful defaults.
func New() Model {
keyStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#909090",
Dark: "#626262",
})
re *lipgloss.Renderer
}

descStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#B2B2B2",
Dark: "#4A4A4A",
})
// Option is used to set options in New.
type Option func(*Model)

sepStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
Light: "#DDDADA",
Dark: "#3C3C3C",
})
// WithRenderer sets the Lip Gloss renderer for the help model.
func WithRenderer(r *lipgloss.Renderer) Option {
return func(m *Model) {
m.re = r
}
}

return Model{
// New creates a new help view with some useful defaults.
func New(opts ...Option) Model {
m := Model{
ShortSeparator: " • ",
FullSeparator: " ",
Ellipsis: "…",
Styles: Styles{
ShortKey: keyStyle,
ShortDesc: descStyle,
ShortSeparator: sepStyle,
Ellipsis: sepStyle.Copy(),
FullKey: keyStyle.Copy(),
FullDesc: descStyle.Copy(),
FullSeparator: sepStyle.Copy(),
},
}

for _, opt := range opts {
opt(&m)
}

if m.re == nil {
m.re = lipgloss.DefaultRenderer()
}

m.Styles = DefaultStyles().Renderer(m.re)

return m
}

// NewModel creates a new help view with some useful defaults.
Expand Down
73 changes: 47 additions & 26 deletions list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,20 @@
}[f]
}

// Option is used to set options in New.
type Option func(*Model)

// WithRenderer sets the Lip Gloss renderer for the list model.
func WithRenderer(r *lipgloss.Renderer) Option {
return func(m *Model) {
m.re = r
}
}

// Model contains the state of this component.
type Model struct {
re *lipgloss.Renderer

showTitle bool
showFilter bool
showStatusBar bool
Expand Down Expand Up @@ -195,8 +207,37 @@
}

// New returns a new model with sensible defaults.
func New(items []Item, delegate ItemDelegate, width, height int) Model {
styles := DefaultStyles()
func New(items []Item, delegate ItemDelegate, width, height int, opts ...Option) Model {
m := Model{
showTitle: true,
showFilter: true,
showStatusBar: true,
showPagination: true,
showHelp: true,
itemNameSingular: "item",
itemNamePlural: "items",
filteringEnabled: true,
KeyMap: DefaultKeyMap(),
Filter: DefaultFilter,
Title: "List",
StatusMessageLifetime: time.Second,

width: width,
height: height,
delegate: delegate,
items: items,
Help: help.New(),
}

for _, opt := range opts {
opt(&m)
}

if m.re == nil {
m.re = lipgloss.DefaultRenderer()
}

styles := DefaultStyles().Renderer(m.re)

sp := spinner.New()
sp.Spinner = spinner.Line
Expand All @@ -214,30 +255,10 @@
p.ActiveDot = styles.ActivePaginationDot.String()
p.InactiveDot = styles.InactivePaginationDot.String()

m := Model{
showTitle: true,
showFilter: true,
showStatusBar: true,
showPagination: true,
showHelp: true,
itemNameSingular: "item",
itemNamePlural: "items",
filteringEnabled: true,
KeyMap: DefaultKeyMap(),
Filter: DefaultFilter,
Styles: styles,
Title: "List",
FilterInput: filterInput,
StatusMessageLifetime: time.Second,

width: width,
height: height,
delegate: delegate,
items: items,
Paginator: p,
spinner: sp,
Help: help.New(),
}
m.Styles = styles
m.FilterInput = filterInput
m.Paginator = p
m.spinner = sp

m.updatePagination()
m.updateKeybindings()
Expand Down Expand Up @@ -679,7 +700,7 @@

// Set keybindings according to the filter state.
func (m *Model) updateKeybindings() {
switch m.filterState {

Check failure on line 703 in list/list.go

View workflow job for this annotation

GitHub Actions / lint-soft

missing cases in switch of type list.FilterState: list.Unfiltered, list.FilterApplied (exhaustive)
case Filtering:
m.KeyMap.CursorUp.SetEnabled(false)
m.KeyMap.CursorDown.SetEnabled(false)
Expand Down Expand Up @@ -1110,7 +1131,7 @@

itemsDisplay := fmt.Sprintf("%d %s", visibleItems, itemName)

if m.filterState == Filtering {

Check failure on line 1134 in list/list.go

View workflow job for this annotation

GitHub Actions / lint-soft

`if m.filterState == Filtering` has complex nested blocks (complexity: 5) (nestif)
// Filter results
if visibleItems == 0 {
status = m.Styles.StatusEmpty.Render("Nothing matched")
Expand Down
22 changes: 22 additions & 0 deletions list/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ type Styles struct {
DividerDot lipgloss.Style
}

// Renderer returns a copy of Styles with the given Lip Gloss renderer set.
func (s Styles) Renderer(r *lipgloss.Renderer) Styles {
s.TitleBar = s.TitleBar.Copy().Renderer(r)
s.Title = s.Title.Copy().Renderer(r)
s.Spinner = s.Spinner.Copy().Renderer(r)
s.FilterPrompt = s.FilterPrompt.Copy().Renderer(r)
s.FilterCursor = s.FilterCursor.Copy().Renderer(r)
s.DefaultFilterCharacterMatch = s.DefaultFilterCharacterMatch.Copy().Renderer(r)
s.StatusBar = s.StatusBar.Copy().Renderer(r)
s.StatusEmpty = s.StatusEmpty.Copy().Renderer(r)
s.StatusBarActiveFilter = s.StatusBarActiveFilter.Copy().Renderer(r)
s.StatusBarFilterCount = s.StatusBarFilterCount.Copy().Renderer(r)
s.NoItems = s.NoItems.Copy().Renderer(r)
s.PaginationStyle = s.PaginationStyle.Copy().Renderer(r)
s.HelpStyle = s.HelpStyle.Copy().Renderer(r)
s.ActivePaginationDot = s.ActivePaginationDot.Copy().Renderer(r)
s.InactivePaginationDot = s.InactivePaginationDot.Copy().Renderer(r)
s.ArabicPagination = s.ArabicPagination.Copy().Renderer(r)
s.DividerDot = s.DividerDot.Copy().Renderer(r)
return s
}

// DefaultStyles returns a set of default style definitions for this list
// component.
func DefaultStyles() (s Styles) {
Expand Down
Loading
Loading