-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lip Gloss ships with a list rendering sub-package. ```go import "github.com/charmbracelet/lipgloss/list" ``` Define a new list. ```go l := list.New("A", "B", "C") ``` Print the list. ```go fmt.Println(l) // • A // • B // • C ``` <!-- Lists have the ability to nest. ```go l := list.New( "A", list.New("Artichoke"), "B", list.New("Baking Flour", "Bananas", "Barley", "Bean Sprouts"), "C", list.New("Cashew Apple", "Cashews", "Coconut Milk", "Curry Paste", "Currywurst"), "D", list.New("Dill", "Dragonfruit", "Dried Shrimp"), "E", list.New("Eggs"), "F", list.New("Fish Cake", "Furikake"), "J", list.New("Jicama"), "K", list.New("Kohlrabi"), "L", list.New("Leeks", "Lentils", "Licorice Root"), ) ``` Print the list. ```go fmt.Println(l) ``` <p align="center"> <img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/0dc9f440-0748-4151-a3b0-7dcf29dfcdb0"> </p> --> Lists can be customized via their enumeration function as well as using `lipgloss.Style`s. ```go enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1) itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1) l := list.New( "Glossier", "Claire’s Boutique", "Nyx", "Mac", "Milk", ). Enumerator(list.Roman). EnumeratorStyle(enumeratorStyle). ItemStyle(itemStyle) ``` Print the list. <p align="center"> <img width="600" alt="List example" src="https://github.com/charmbracelet/lipgloss/assets/42545625/360494f1-57fb-4e13-bc19-0006efe01561"> </p> In addition to the predefined enumerators (`Arabic`, `Alphabet`, `Roman`, `Bullet`, `Tree`), you may also define your own custom enumerator: ```go var DuckDuckGooseEnumerator Enumerator = func(l *List, i int) string { if l.At(i) == "Goose" { return "Honk →" } return "" } ``` Use it in a list: ```go l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck") l.Enumerator(DuckDuckGooseEnumerator) ``` Print the list: <p align="center"> <img width="600" alt="image" src="https://github.com/charmbracelet/lipgloss/assets/42545625/157aaf30-140d-4948-9bb4-dfba46e5b87e"> </p> If you need, you can also build lists incrementally: ```go l := list.New() for i := 0; i < repeat; i++ { l.Item("Lip Gloss") } ```
- Loading branch information
1 parent
ce5323e
commit 74a5b96
Showing
7 changed files
with
739 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/charmbracelet/lipgloss" | ||
"github.com/charmbracelet/lipgloss/list" | ||
) | ||
|
||
func main() { | ||
l := list.New("Duck", "Duck", "Duck", "Duck", "Goose", "Duck", "Duck") | ||
|
||
var DuckDuckGooseEnumerator = func(i int) string { | ||
if l.At(i) == "Goose" { | ||
return "Honk →" | ||
} | ||
return "" | ||
} | ||
|
||
l = l.Enumerator(DuckDuckGooseEnumerator).EnumeratorStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("48")).MarginRight(1)) | ||
|
||
fmt.Println(l) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/charmbracelet/lipgloss" | ||
"github.com/charmbracelet/lipgloss/list" | ||
) | ||
|
||
func main() { | ||
enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1) | ||
itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1) | ||
|
||
l := list.New( | ||
"Glossier", | ||
"Claire’s Boutique", | ||
"Nyx", | ||
"Mac", | ||
"Milk", | ||
). | ||
Enumerator(list.Roman). | ||
EnumeratorStyle(enumeratorStyle). | ||
ItemStyle(itemStyle) | ||
|
||
fmt.Println(l.String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package list | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Enumerator defines a function that returns the correct prefix for the list | ||
// element at the given index. | ||
type Enumerator func(i int) string | ||
|
||
const abcLen = 26 | ||
|
||
// Alphabet is the enumeration for alphabetical listing. | ||
// | ||
// a. Foo | ||
// b. Bar | ||
// c. Baz | ||
// d. Qux. | ||
func Alphabet(i int) string { | ||
if i >= abcLen*abcLen+abcLen { | ||
return fmt.Sprintf("%c%c%c.", 'A'+i/abcLen/abcLen-1, 'A'+(i/abcLen)%abcLen-1, 'A'+i%abcLen) | ||
} | ||
if i >= abcLen { | ||
return fmt.Sprintf("%c%c.", 'A'+i/abcLen-1, 'A'+(i)%abcLen) | ||
} | ||
return fmt.Sprintf("%c.", 'A'+i%abcLen) | ||
} | ||
|
||
// Arabic is the enumeration for arabic numerals listing. | ||
// | ||
// 1. Foo | ||
// 2. Bar | ||
// 3. Baz | ||
// 4. Qux. | ||
func Arabic(i int) string { | ||
return fmt.Sprintf("%d.", i+1) | ||
} | ||
|
||
var ( | ||
roman = []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} | ||
arabic = []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} | ||
) | ||
|
||
// Roman is the enumeration for roman numerals listing. | ||
// | ||
// / I. Foo | ||
// / II. Bar | ||
// / III. Baz | ||
// / IV. Qux. | ||
func Roman(i int) string { | ||
var result strings.Builder | ||
|
||
for v, value := range arabic { | ||
for i >= value-1 { | ||
i -= value | ||
result.WriteString(roman[v]) | ||
} | ||
} | ||
result.WriteRune('.') | ||
return result.String() | ||
} | ||
|
||
// Bullet is the enumeration for bullet listing. | ||
// | ||
// • Foo | ||
// • Bar | ||
// • Baz | ||
// • Qux. | ||
func Bullet(_ int) string { | ||
return "•" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Package list defines an API to build lists. | ||
// | ||
// A list is a enumerated collection of items. Items are rendered vertically | ||
// stacked on top of each other. Like the following: | ||
// | ||
// list.New("A", "B", "C"). | ||
// (list.Bullet). | ||
// String() | ||
// | ||
// • A | ||
// • B | ||
// • C | ||
package list | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/charmbracelet/lipgloss" | ||
) | ||
|
||
// Item is a list item. It allows the list to print it's elements, a list can | ||
// contain nested lists. | ||
type Item interface { | ||
any | List | ||
} | ||
|
||
// List is the representation of a lipgloss List. | ||
// | ||
// It can be printed to display a human-readable list. | ||
// | ||
// fmt.Println(list.New("A", "B", "C")) | ||
// | ||
// • A | ||
// • B | ||
// • C | ||
type List struct { | ||
indent int | ||
separator lipgloss.Style | ||
items []Item | ||
itemStyleFunc StyleFunc | ||
enumerator Enumerator | ||
enumeratorStyleFunc StyleFunc | ||
} | ||
|
||
// StyleFunc defines a list style function that returns the correct style for | ||
// the list element at the given index. | ||
type StyleFunc func(i int) lipgloss.Style | ||
|
||
// New returns a new list given the list items. List items may be of any | ||
// primitive type and other Lists. | ||
// | ||
// list.New( | ||
// "Foo", | ||
// "Bar", | ||
// NewBaz{}, | ||
// list.New( | ||
// "Qux", | ||
// "Quux", | ||
// ), | ||
// ) | ||
func New(items ...Item) List { | ||
return List{ | ||
items: items, | ||
separator: lipgloss.NewStyle().SetString("\n"), | ||
enumerator: func(_ int) string { return "•" }, | ||
enumeratorStyleFunc: func(_ int) lipgloss.Style { | ||
return lipgloss.NewStyle().MarginRight(1) | ||
}, | ||
} | ||
} | ||
|
||
// Item appends the given item to the list. | ||
func (l List) Item(item Item) List { | ||
l.items = append(l.items, item) | ||
return l | ||
} | ||
|
||
// Separator sets the list separator style, which separates list items. | ||
// The default separator is a newline. | ||
func (l List) Separator(s lipgloss.Style) List { | ||
l.separator = s | ||
return l | ||
} | ||
|
||
// Prefix sets a static enumerator. | ||
// | ||
// list.New(...).Prefix(s) is equivalent to: | ||
// | ||
// list.New(...).Enumerator(func(_ int) string { | ||
// return s | ||
// }) | ||
func (l List) Prefix(s string) List { | ||
l.enumerator = func(_ int) string { | ||
return s | ||
} | ||
return l | ||
} | ||
|
||
// Enumerator sets list enumeration function. The function will be called with | ||
// the index of the item in the list. | ||
func (l List) Enumerator(e Enumerator) List { | ||
l.enumerator = e | ||
return l | ||
} | ||
|
||
// EnumeratorStyleFunc sets the style function for the list enumeration. The | ||
// function will be called with the index of the item in the list. | ||
func (l List) EnumeratorStyleFunc(f StyleFunc) List { | ||
l.enumeratorStyleFunc = f | ||
return l | ||
} | ||
|
||
// ItemStyleFunc sets the style function for the list enumeration. The | ||
// function will be called with the index of the item in the list. | ||
func (l List) ItemStyleFunc(f StyleFunc) List { | ||
l.itemStyleFunc = f | ||
return l | ||
} | ||
|
||
// EnumeratorStyle sets the style for the list enumeration. The function will be | ||
// called with the index of the item in the list. | ||
func (l List) EnumeratorStyle(style lipgloss.Style) List { | ||
l.enumeratorStyleFunc = func(_ int) lipgloss.Style { return style } | ||
return l | ||
} | ||
|
||
// ItemStyle sets the style function for the list enumeration. The | ||
// function will be called with the index of the item in the list. | ||
func (l List) ItemStyle(style lipgloss.Style) List { | ||
l.itemStyleFunc = func(_ int) lipgloss.Style { return style } | ||
return l | ||
} | ||
|
||
// At returns the item at index i. | ||
func (l List) At(i int) Item { | ||
if i >= 0 && i < len(l.items) { | ||
return l.items[i] | ||
} | ||
return nil | ||
} | ||
|
||
const indent = 2 | ||
|
||
// String returns a string representation of the list. | ||
func (l List) String() string { | ||
var s strings.Builder | ||
|
||
// find the longest enumerator value of this list. | ||
last := len(l.items) - 1 | ||
var maxLen int | ||
for i := 0; i <= last; i++ { | ||
enum := l.enumeratorStyleFunc(i).Render(l.enumerator(i)) | ||
maxLen = max(lipgloss.Width(enum), maxLen) | ||
} | ||
|
||
for i, item := range l.items { | ||
switch item := item.(type) { | ||
case List: | ||
item.indent = l.indent + indent | ||
s.WriteString(item.String()) | ||
if i != last { | ||
s.WriteString(l.separator.String()) | ||
} | ||
default: | ||
indent := strings.Repeat(" ", l.indent) | ||
enumerator := l.enumeratorStyleFunc(i). | ||
Width(maxLen - 1). | ||
Align(lipgloss.Right). | ||
Render(l.enumerator(i)) | ||
listItem := lipgloss.JoinHorizontal( | ||
lipgloss.Top, | ||
indent, | ||
enumerator, | ||
fmt.Sprintf("%v", item), | ||
) | ||
s.WriteString(listItem) | ||
if i != last { | ||
s.WriteString(l.separator.String()) | ||
} | ||
} | ||
} | ||
return s.String() | ||
} | ||
|
||
func max(a, b int) int { | ||
if a > b { | ||
return a | ||
} | ||
return b | ||
} |
Oops, something went wrong.