mirror of
https://github.com/Maks1mS/bubbles.git
synced 2024-12-25 07:04:37 +03:00
Add list bubble
This commit is contained in:
parent
0be588365e
commit
4e18245481
27
README.md
27
README.md
@ -10,12 +10,13 @@ Bubbles
|
|||||||
[![Build Status](https://github.com/charmbracelet/bubbles/workflows/build/badge.svg)](https://github.com/charmbracelet/bubbles/actions)
|
[![Build Status](https://github.com/charmbracelet/bubbles/workflows/build/badge.svg)](https://github.com/charmbracelet/bubbles/actions)
|
||||||
[![Go ReportCard](https://goreportcard.com/badge/charmbracelet/bubbles)](https://goreportcard.com/report/charmbracelet/bubbles)
|
[![Go ReportCard](https://goreportcard.com/badge/charmbracelet/bubbles)](https://goreportcard.com/report/charmbracelet/bubbles)
|
||||||
|
|
||||||
Some components for [Bubble Tea](https://github.com/charmbracelet/bubbletea) applications.
|
Some components for [Bubble Tea](https://github.com/charmbracelet/bubbletea)
|
||||||
|
applications. These components are used in production in [Glow][glow],
|
||||||
These components are used in production in [Glow][glow] and [Charm][charm].
|
[Charm][charm] and [many other applications][otherstuff].
|
||||||
|
|
||||||
[glow]: https://github.com/charmbracelet/glow
|
[glow]: https://github.com/charmbracelet/glow
|
||||||
[charm]: https://github.com/charmbracelet/charm
|
[charm]: https://github.com/charmbracelet/charm
|
||||||
|
[otherstuff]: https://github.com/charmbracelet/bubbletea/#bubble-tea-in-the-wild
|
||||||
|
|
||||||
|
|
||||||
## Spinner
|
## Spinner
|
||||||
@ -25,7 +26,8 @@ These components are used in production in [Glow][glow] and [Charm][charm].
|
|||||||
A spinner, useful for indicating that some kind an operation is happening.
|
A spinner, useful for indicating that some kind an operation is happening.
|
||||||
There are a couple default ones, but you can also pass your own ”frames.”
|
There are a couple default ones, but you can also pass your own ”frames.”
|
||||||
|
|
||||||
* [Example code](https://github.com/charmbracelet/tea/tree/master/examples/spinner/main.go)
|
* [Example code, basic spinner](https://github.com/charmbracelet/tea/tree/master/examples/spinner/main.go)
|
||||||
|
* [Example code, various spinners](https://github.com/charmbracelet/tea/tree/master/examples/spinners/main.go)
|
||||||
|
|
||||||
|
|
||||||
## Text Input
|
## Text Input
|
||||||
@ -60,8 +62,7 @@ Supports "dot-style" pagination (similar to what you might see on iOS) and
|
|||||||
numeric page numbering, but you could also just use this component for the
|
numeric page numbering, but you could also just use this component for the
|
||||||
logic and visualize pagination however you like.
|
logic and visualize pagination however you like.
|
||||||
|
|
||||||
This component is used in [Glow][glow] to browse documents and [Charm][charm] to
|
* [Example code](https://github.com/charmbracelet/bubbletea/blob/master/examples/pager/main.go)
|
||||||
browse SSH keys.
|
|
||||||
|
|
||||||
|
|
||||||
## Viewport
|
## Viewport
|
||||||
@ -80,6 +81,20 @@ indenting and text wrapping.
|
|||||||
[reflow]: https://github.com/muesli/reflow
|
[reflow]: https://github.com/muesli/reflow
|
||||||
|
|
||||||
|
|
||||||
|
## List
|
||||||
|
|
||||||
|
<img src="https://stuff.charm.sh/bubbles-examples/list.gif" width="600" alt="List Example">
|
||||||
|
|
||||||
|
A customizable, batteries-included component for browsing a set of items.
|
||||||
|
Features pagination, fuzzy filtering, auto-generated help, an activity spinner,
|
||||||
|
and status messages, all of which and be enabled and disabled as needed.
|
||||||
|
Extrapolated from [Glow][glow].
|
||||||
|
|
||||||
|
* [Example code, default list](https://github.com/charmbracelet/tea/tree/master/examples/list-default/main.go)
|
||||||
|
* [Example code, simple list](https://github.com/charmbracelet/tea/tree/master/examples/list-simple/main.go)
|
||||||
|
* [Example code, all features](https://github.com/charmbracelet/tea/tree/master/examples/list-fancy/main.go)
|
||||||
|
|
||||||
|
|
||||||
## Help
|
## Help
|
||||||
|
|
||||||
A customizable horizontal mini help view that automatically generates itself
|
A customizable horizontal mini help view that automatically generates itself
|
||||||
|
4
go.mod
4
go.mod
@ -6,10 +6,12 @@ require (
|
|||||||
github.com/atotto/clipboard v0.1.2
|
github.com/atotto/clipboard v0.1.2
|
||||||
github.com/charmbracelet/bubbletea v0.13.1
|
github.com/charmbracelet/bubbletea v0.13.1
|
||||||
github.com/charmbracelet/harmonica v0.1.0
|
github.com/charmbracelet/harmonica v0.1.0
|
||||||
github.com/charmbracelet/lipgloss v0.1.2
|
github.com/charmbracelet/lipgloss v0.2.2-0.20210525180645-66eb23093aa6
|
||||||
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||||
github.com/mattn/go-runewidth v0.0.13
|
github.com/mattn/go-runewidth v0.0.13
|
||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68
|
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68
|
||||||
github.com/muesli/termenv v0.8.1
|
github.com/muesli/termenv v0.8.1
|
||||||
|
github.com/sahilm/fuzzy v0.1.0
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
|
||||||
)
|
)
|
||||||
|
12
go.sum
12
go.sum
@ -4,11 +4,13 @@ github.com/charmbracelet/bubbletea v0.13.1 h1:huvX8mPaeMZ8DLulT50iEWRF+iitY5FNED
|
|||||||
github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg=
|
github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg=
|
||||||
github.com/charmbracelet/harmonica v0.1.0 h1:lFKeSd6OAckQ/CEzPVd2mqj+YMEubQ/3FM2IYY3xNm0=
|
github.com/charmbracelet/harmonica v0.1.0 h1:lFKeSd6OAckQ/CEzPVd2mqj+YMEubQ/3FM2IYY3xNm0=
|
||||||
github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||||
github.com/charmbracelet/lipgloss v0.1.2 h1:D+LUMg34W7n2pkuMrevKVxT7HXqnoRHm7IoomkX3/ZU=
|
github.com/charmbracelet/lipgloss v0.2.2-0.20210525180645-66eb23093aa6 h1:lAHD8PDu2W7USlmKEt2v1/BCfmShVXrijjbCQcofOmg=
|
||||||
github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg=
|
github.com/charmbracelet/lipgloss v0.2.2-0.20210525180645-66eb23093aa6/go.mod h1:uiZUfrHLQN14I0lJ5591WtcHyY1X76pOIPSaRKPY6dE=
|
||||||
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
|
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
|
||||||
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
|
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
|
||||||
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
@ -16,6 +18,7 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk=
|
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk=
|
||||||
@ -26,8 +29,13 @@ github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/f
|
|||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||||
|
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||||
|
205
list/defaultitem.go
Normal file
205
list/defaultitem.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/bubbles/key"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/muesli/reflow/truncate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultItemStyles defines styling for a default list item.
|
||||||
|
// See DefaultItemView for when these come into play.
|
||||||
|
type DefaultItemStyles struct {
|
||||||
|
// The Normal state.
|
||||||
|
NormalTitle lipgloss.Style
|
||||||
|
NormalDesc lipgloss.Style
|
||||||
|
|
||||||
|
// The selected item state.
|
||||||
|
SelectedTitle lipgloss.Style
|
||||||
|
SelectedDesc lipgloss.Style
|
||||||
|
|
||||||
|
// The dimmed state, for when the filter input is initially activated.
|
||||||
|
DimmedTitle lipgloss.Style
|
||||||
|
DimmedDesc lipgloss.Style
|
||||||
|
|
||||||
|
// Charcters matching the current filter, if any.
|
||||||
|
FilterMatch lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultItemStyles returns style definitions for a default item. See
|
||||||
|
// DefaultItemView for when these come into play.
|
||||||
|
func NewDefaultItemStyles() (s DefaultItemStyles) {
|
||||||
|
s.NormalTitle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}).
|
||||||
|
Padding(0, 0, 0, 2)
|
||||||
|
|
||||||
|
s.NormalDesc = s.NormalTitle.Copy().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"})
|
||||||
|
|
||||||
|
s.SelectedTitle = lipgloss.NewStyle().
|
||||||
|
Border(lipgloss.NormalBorder(), false, false, false, true).
|
||||||
|
BorderForeground(lipgloss.AdaptiveColor{Light: "#F793FF", Dark: "#AD58B4"}).
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"}).
|
||||||
|
Padding(0, 0, 0, 1)
|
||||||
|
|
||||||
|
s.SelectedDesc = s.SelectedTitle.Copy().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#F793FF", Dark: "#AD58B4"})
|
||||||
|
|
||||||
|
s.DimmedTitle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"}).
|
||||||
|
Padding(0, 0, 0, 2)
|
||||||
|
|
||||||
|
s.DimmedDesc = s.DimmedTitle.Copy().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#C2B8C2", Dark: "#4D4D4D"})
|
||||||
|
|
||||||
|
s.FilterMatch = lipgloss.NewStyle().Underline(true)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultItem describes an items designed to work with DefaultDelegate.
|
||||||
|
type DefaultItem interface {
|
||||||
|
Item
|
||||||
|
Title() string
|
||||||
|
Description() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDelegate is a standard delegate designed to work in lists. It's
|
||||||
|
// styled by DefaultItemStyles, which can be customized as you like.
|
||||||
|
//
|
||||||
|
// The description line can be hidden by setting Description to false, which
|
||||||
|
// renders the list as single-line-items. The spacing between items can be set
|
||||||
|
// with the SetSpacing method.
|
||||||
|
//
|
||||||
|
// Setting UpdateFunc is optional. If it's set it will be called when the
|
||||||
|
// ItemDelegate called, which is called when the list's Update function is
|
||||||
|
// invoked.
|
||||||
|
//
|
||||||
|
// Settings ShortHelpFunc and FullHelpFunc is optional. They can can be set to
|
||||||
|
// include items in the list's default short and full help menus.
|
||||||
|
type DefaultDelegate struct {
|
||||||
|
ShowDescription bool
|
||||||
|
Styles DefaultItemStyles
|
||||||
|
UpdateFunc func(tea.Msg, *Model) tea.Cmd
|
||||||
|
ShortHelpFunc func() []key.Binding
|
||||||
|
FullHelpFunc func() [][]key.Binding
|
||||||
|
spacing int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultDelegate creates a new delegate with default styles.
|
||||||
|
func NewDefaultDelegate() DefaultDelegate {
|
||||||
|
return DefaultDelegate{
|
||||||
|
ShowDescription: true,
|
||||||
|
Styles: NewDefaultItemStyles(),
|
||||||
|
spacing: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Height returns the delegate's preferred height.
|
||||||
|
func (d DefaultDelegate) Height() int {
|
||||||
|
if d.ShowDescription {
|
||||||
|
return 2 //nolint:gomnd
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSpacing set the delegate's spacing.
|
||||||
|
func (d *DefaultDelegate) SetSpacing(i int) {
|
||||||
|
d.spacing = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spacing returns the delegate's spacing.
|
||||||
|
func (d DefaultDelegate) Spacing() int {
|
||||||
|
return d.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update checks whether the delegate's UpdateFunc is set and calls it.
|
||||||
|
func (d DefaultDelegate) Update(msg tea.Msg, m *Model) tea.Cmd {
|
||||||
|
if d.UpdateFunc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.UpdateFunc(msg, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render prints an item.
|
||||||
|
func (d DefaultDelegate) Render(w io.Writer, m Model, index int, item Item) {
|
||||||
|
var (
|
||||||
|
title, desc string
|
||||||
|
matchedRunes []int
|
||||||
|
s = &d.Styles
|
||||||
|
)
|
||||||
|
|
||||||
|
if i, ok := item.(DefaultItem); ok {
|
||||||
|
title = i.Title()
|
||||||
|
desc = i.Description()
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent text from exceeding list width
|
||||||
|
if m.width > 0 {
|
||||||
|
textwidth := uint(m.width - s.NormalTitle.GetPaddingLeft() - s.NormalTitle.GetPaddingRight())
|
||||||
|
title = truncate.StringWithTail(title, textwidth, ellipsis)
|
||||||
|
desc = truncate.StringWithTail(desc, textwidth, ellipsis)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditions
|
||||||
|
var (
|
||||||
|
isSelected = index == m.Index()
|
||||||
|
emptyFilter = m.FilterState() == Filtering && m.FilterValue() == ""
|
||||||
|
isFiltered = m.FilterState() == Filtering || m.FilterState() == FilterApplied
|
||||||
|
)
|
||||||
|
|
||||||
|
if isFiltered && index < len(m.filteredItems) {
|
||||||
|
// Get indices of matched characters
|
||||||
|
matchedRunes = m.MatchesForItem(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if emptyFilter {
|
||||||
|
title = s.DimmedTitle.Render(title)
|
||||||
|
desc = s.DimmedDesc.Render(desc)
|
||||||
|
} else if isSelected && m.FilterState() != Filtering {
|
||||||
|
if isFiltered {
|
||||||
|
// Highlight matches
|
||||||
|
unmatched := s.SelectedTitle.Inline(true)
|
||||||
|
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
||||||
|
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||||
|
}
|
||||||
|
title = s.SelectedTitle.Render(title)
|
||||||
|
desc = s.SelectedDesc.Render(desc)
|
||||||
|
} else {
|
||||||
|
if isFiltered {
|
||||||
|
// Highlight matches
|
||||||
|
unmatched := s.NormalTitle.Inline(true)
|
||||||
|
matched := unmatched.Copy().Inherit(s.FilterMatch)
|
||||||
|
title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched)
|
||||||
|
}
|
||||||
|
title = s.NormalTitle.Render(title)
|
||||||
|
desc = s.NormalDesc.Render(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.ShowDescription {
|
||||||
|
fmt.Fprintf(w, "%s\n%s", title, desc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShortHelp returns the delegate's short help.
|
||||||
|
func (d DefaultDelegate) ShortHelp() []key.Binding {
|
||||||
|
if d.ShortHelpFunc != nil {
|
||||||
|
return d.ShortHelpFunc()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullHelp returns the delegate's full help.
|
||||||
|
func (d DefaultDelegate) FullHelp() [][]key.Binding {
|
||||||
|
if d.FullHelpFunc != nil {
|
||||||
|
return d.FullHelpFunc()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
97
list/keys.go
Normal file
97
list/keys.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import "github.com/charmbracelet/bubbles/key"
|
||||||
|
|
||||||
|
// KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which
|
||||||
|
// is used to render the menu menu.
|
||||||
|
type KeyMap struct {
|
||||||
|
// Keybindings used when browsing the list.
|
||||||
|
CursorUp key.Binding
|
||||||
|
CursorDown key.Binding
|
||||||
|
NextPage key.Binding
|
||||||
|
PrevPage key.Binding
|
||||||
|
GoToStart key.Binding
|
||||||
|
GoToEnd key.Binding
|
||||||
|
Filter key.Binding
|
||||||
|
ClearFilter key.Binding
|
||||||
|
|
||||||
|
// Keybindings used when setting a filter.
|
||||||
|
CancelWhileFiltering key.Binding
|
||||||
|
AcceptWhileFiltering key.Binding
|
||||||
|
|
||||||
|
// Help toggle keybindings.
|
||||||
|
ShowFullHelp key.Binding
|
||||||
|
CloseFullHelp key.Binding
|
||||||
|
|
||||||
|
// The quit keybinding. This won't be caught when filtering.
|
||||||
|
Quit key.Binding
|
||||||
|
|
||||||
|
// The quit-no-matter-what keybinding. This will be caught when filtering.
|
||||||
|
ForceQuit key.Binding
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultKeyMap returns a default set of keybindings.
|
||||||
|
func DefaultKeyMap() KeyMap {
|
||||||
|
return KeyMap{
|
||||||
|
// Browsing.
|
||||||
|
CursorUp: key.NewBinding(
|
||||||
|
key.WithKeys("up", "k"),
|
||||||
|
key.WithHelp("↑/k", "up"),
|
||||||
|
),
|
||||||
|
CursorDown: key.NewBinding(
|
||||||
|
key.WithKeys("down", "j"),
|
||||||
|
key.WithHelp("↓/j", "down"),
|
||||||
|
),
|
||||||
|
PrevPage: key.NewBinding(
|
||||||
|
key.WithKeys("left", "h", "pgup", "b", "u"),
|
||||||
|
key.WithHelp("←/h/pgup", "prev page"),
|
||||||
|
),
|
||||||
|
NextPage: key.NewBinding(
|
||||||
|
key.WithKeys("right", "l", "pgdown", "f", "d"),
|
||||||
|
key.WithHelp("→/l/pgdn", "next page"),
|
||||||
|
),
|
||||||
|
GoToStart: key.NewBinding(
|
||||||
|
key.WithKeys("home", "g"),
|
||||||
|
key.WithHelp("g/home", "go to start"),
|
||||||
|
),
|
||||||
|
GoToEnd: key.NewBinding(
|
||||||
|
key.WithKeys("end", "G"),
|
||||||
|
key.WithHelp("G/end", "go to end"),
|
||||||
|
),
|
||||||
|
Filter: key.NewBinding(
|
||||||
|
key.WithKeys("/"),
|
||||||
|
key.WithHelp("/", "filter"),
|
||||||
|
),
|
||||||
|
ClearFilter: key.NewBinding(
|
||||||
|
key.WithKeys("esc"),
|
||||||
|
key.WithHelp("esc", "clear filter"),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Filtering.
|
||||||
|
CancelWhileFiltering: key.NewBinding(
|
||||||
|
key.WithKeys("esc"),
|
||||||
|
key.WithHelp("esc", "cancel"),
|
||||||
|
),
|
||||||
|
AcceptWhileFiltering: key.NewBinding(
|
||||||
|
key.WithKeys("enter", "tab", "shift+tab", "ctrl+k", "up", "ctrl+j", "down"),
|
||||||
|
key.WithHelp("enter", "apply filter"),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Toggle help.
|
||||||
|
ShowFullHelp: key.NewBinding(
|
||||||
|
key.WithKeys("?"),
|
||||||
|
key.WithHelp("?", "more"),
|
||||||
|
),
|
||||||
|
CloseFullHelp: key.NewBinding(
|
||||||
|
key.WithKeys("?"),
|
||||||
|
key.WithHelp("?", "close help"),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Quitting.
|
||||||
|
Quit: key.NewBinding(
|
||||||
|
key.WithKeys("q", "esc"),
|
||||||
|
key.WithHelp("q", "quit"),
|
||||||
|
),
|
||||||
|
ForceQuit: key.NewBinding(key.WithKeys("ctrl+c")),
|
||||||
|
}
|
||||||
|
}
|
1197
list/list.go
Normal file
1197
list/list.go
Normal file
File diff suppressed because it is too large
Load Diff
99
list/style.go
Normal file
99
list/style.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bullet = "•"
|
||||||
|
ellipsis = "…"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Styles contains style definitions for this list component. By default, these
|
||||||
|
// values are generated by DefaultStyles.
|
||||||
|
type Styles struct {
|
||||||
|
TitleBar lipgloss.Style
|
||||||
|
Title lipgloss.Style
|
||||||
|
Spinner lipgloss.Style
|
||||||
|
FilterPrompt lipgloss.Style
|
||||||
|
FilterCursor lipgloss.Style
|
||||||
|
|
||||||
|
// Default styling for matched characters in a filter. This can be
|
||||||
|
// overridden by delegates.
|
||||||
|
DefaultFilterCharacterMatch lipgloss.Style
|
||||||
|
|
||||||
|
StatusBar lipgloss.Style
|
||||||
|
StatusEmpty lipgloss.Style
|
||||||
|
StatusBarActiveFilter lipgloss.Style
|
||||||
|
StatusBarFilterCount lipgloss.Style
|
||||||
|
|
||||||
|
NoItems lipgloss.Style
|
||||||
|
|
||||||
|
PaginationStyle lipgloss.Style
|
||||||
|
HelpStyle lipgloss.Style
|
||||||
|
|
||||||
|
// Styled characters.
|
||||||
|
ActivePaginationDot lipgloss.Style
|
||||||
|
InactivePaginationDot lipgloss.Style
|
||||||
|
ArabicPagination lipgloss.Style
|
||||||
|
DividerDot lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultStyles returns a set of default style definitions for this list
|
||||||
|
// component.
|
||||||
|
func DefaultStyles() (s Styles) {
|
||||||
|
verySubduedColor := lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"}
|
||||||
|
subduedColor := lipgloss.AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}
|
||||||
|
|
||||||
|
s.TitleBar = lipgloss.NewStyle().Padding(0, 0, 1, 2)
|
||||||
|
|
||||||
|
s.Title = lipgloss.NewStyle().
|
||||||
|
Background(lipgloss.Color("62")).
|
||||||
|
Foreground(lipgloss.Color("230")).
|
||||||
|
Padding(0, 1)
|
||||||
|
|
||||||
|
s.Spinner = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#8E8E8E", Dark: "#747373"})
|
||||||
|
|
||||||
|
s.FilterPrompt = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"})
|
||||||
|
|
||||||
|
s.FilterCursor = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"})
|
||||||
|
|
||||||
|
s.DefaultFilterCharacterMatch = lipgloss.NewStyle().Underline(true)
|
||||||
|
|
||||||
|
s.StatusBar = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"}).
|
||||||
|
Padding(0, 0, 1, 2)
|
||||||
|
|
||||||
|
s.StatusEmpty = lipgloss.NewStyle().Foreground(subduedColor)
|
||||||
|
|
||||||
|
s.StatusBarActiveFilter = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"})
|
||||||
|
|
||||||
|
s.StatusBarFilterCount = lipgloss.NewStyle().Foreground(verySubduedColor)
|
||||||
|
|
||||||
|
s.NoItems = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#909090", Dark: "#626262"})
|
||||||
|
|
||||||
|
s.ArabicPagination = lipgloss.NewStyle().Foreground(subduedColor)
|
||||||
|
|
||||||
|
s.PaginationStyle = lipgloss.NewStyle().PaddingLeft(2) //nolint:gomnd
|
||||||
|
|
||||||
|
s.HelpStyle = lipgloss.NewStyle().Padding(1, 0, 0, 2)
|
||||||
|
|
||||||
|
s.ActivePaginationDot = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"}).
|
||||||
|
SetString(bullet)
|
||||||
|
|
||||||
|
s.InactivePaginationDot = lipgloss.NewStyle().
|
||||||
|
Foreground(verySubduedColor).
|
||||||
|
SetString(bullet)
|
||||||
|
|
||||||
|
s.DividerDot = lipgloss.NewStyle().
|
||||||
|
Foreground(verySubduedColor).
|
||||||
|
SetString(" " + bullet + " ")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
@ -35,7 +35,7 @@ type Model struct {
|
|||||||
UseJKKeys bool
|
UseJKKeys bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTotalPages is a helper function for calculatng the total number of pages
|
// SetTotalPages is a helper function for calculating the total number of pages
|
||||||
// from a given number of items. It's use is optional since this pager can be
|
// from a given number of items. It's use is optional since this pager can be
|
||||||
// used for other things beyond navigating sets. Note that it both returns the
|
// used for other things beyond navigating sets. Note that it both returns the
|
||||||
// number of total pages and alters the model.
|
// number of total pages and alters the model.
|
||||||
|
Loading…
Reference in New Issue
Block a user