8 Commits

Author SHA1 Message Date
Gabriel Nagy
14d9f9cd76 fix(textinput): set cursor on initial value
This reverts the code back to the logic from 4ce16e8 which fixed the
issue of the cursor not being moved when an initial value was set with
SetValue.

The fix regressed in 16053f4.
2022-07-05 10:15:12 -04:00
Leandro López
69bf367d37 feat(spinners): Construct new spinners with WithSpinner + WithStyle options (#148)
* Add spinner.New test

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* Add spinner.Option type and spinner.WithSpinner option

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* Allow passing options in spinner.New

This doesn't break existing code as it uses variadic arguments, so
any existing code as the following should continue to work:

    s := spinner.New()
    s.spinner = spinner.Dot

This change allows for instead of those two lines, having a call:

   s := spinner.New(spinner.WithSpinner(spinner.Dot))

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* Add spinner.WithX option for each spinner.Spinner

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* Refactor spinner tests

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* Add spinner.WithStyle option function

Signed-off-by: Leandro López (inkel) <inkel.ar@gmail.com>

* refactor: remove With... Spinner aliases

Co-authored-by: Maas Lalani <maas@lalani.dev>
2022-07-05 10:15:12 -04:00
Kyosuke Fujimoto
2578480343 fix(list): Disable Quit Key (#104) 2022-07-05 10:15:12 -04:00
Pablo Díaz-López
136e1f05bf fix(timer): stop should return cmd (#138) 2022-07-05 10:15:11 -04:00
Charlie Roth
42f85b4a1b docs: fix paginator example link (#177) 2022-06-21 09:23:57 -04:00
vzvu3k6k
4d0a0ea9d8 docs(spinner): correct comment about internal ID (#171) 2022-06-21 09:17:48 -04:00
Weslei Juan Novaes Pereira
658a4febc7 feat: new Hamburger + Meter spinners (#172)
Co-authored-by: Maas Lalani <maas@lalani.dev>
2022-06-21 08:59:23 -04:00
Maas Lalani
93e464296e docs(list): fix linting errors 2022-06-16 18:47:55 -04:00
5 changed files with 110 additions and 9 deletions

View File

@@ -66,7 +66,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
logic and visualize pagination however you like.
* [Example code](https://github.com/charmbracelet/bubbletea/blob/master/examples/pager/main.go)
* [Example code](https://github.com/charmbracelet/bubbletea/blob/master/examples/paginator/main.go)
## Viewport

View File

@@ -8,8 +8,8 @@ import (
"github.com/charmbracelet/lipgloss"
)
// Internal ID management for text inputs. Necessary for blink integrity when
// multiple text inputs are involved.
// Internal ID management. Used during animating to ensure that frame messages
// are received only by spinner components that sent them.
var (
lastID int
idMtx sync.Mutex
@@ -67,6 +67,22 @@ var (
Frames: []string{"🙈", "🙉", "🙊"},
FPS: time.Second / 3, //nolint:gomnd
}
Meter = Spinner{
Frames: []string{
"▱▱▱",
"▰▱▱",
"▰▰▱",
"▰▰▰",
"▰▰▱",
"▰▱▱",
"▱▱▱",
},
FPS: time.Second / 7, //nolint:gomnd
}
Hamburger = Spinner{
Frames: []string{"☱", "☲", "☴", "☲"},
FPS: time.Second / 3, //nolint:gomnd
}
)
// Model contains the state for the spinner. Use NewModel to create new models
@@ -93,11 +109,17 @@ func (m Model) ID() int {
}
// New returns a model with default values.
func New() Model {
return Model{
func New(opts ...Option) Model {
m := Model{
Spinner: Line,
id: nextID(),
}
for _, opt := range opts {
opt(&m)
}
return m
}
// NewModel returns a model with default values.
@@ -183,3 +205,23 @@ func (m Model) tick(id, tag int) tea.Cmd {
func Tick() tea.Msg {
return TickMsg{Time: time.Now()}
}
// Option is used to set options in New. For example:
//
// spinner := New(WithSpinner(Dot))
//
type Option func(*Model)
// WithSpinner is an option to set the spinner.
func WithSpinner(spinner Spinner) Option {
return func(m *Model) {
m.Spinner = spinner
}
}
// WithStyle is an option to set the spinner style.
func WithStyle(style lipgloss.Style) Option {
return func(m *Model) {
m.Style = style
}
}

61
spinner/spinner_test.go Normal file
View File

@@ -0,0 +1,61 @@
package spinner_test
import (
"testing"
"github.com/charmbracelet/bubbles/spinner"
)
func TestSpinnerNew(t *testing.T) {
assertEqualSpinner := func(t *testing.T, exp, got spinner.Spinner) {
t.Helper()
if exp.FPS != got.FPS {
t.Errorf("expecting %d FPS, got %d", exp.FPS, got.FPS)
}
if e, g := len(exp.Frames), len(got.Frames); e != g {
t.Fatalf("expecting %d frames, got %d", e, g)
}
for i, e := range exp.Frames {
if g := got.Frames[i]; e != g {
t.Errorf("expecting frame index %d with value %q, got %q", i, e, g)
}
}
}
t.Run("default", func(t *testing.T) {
s := spinner.New()
assertEqualSpinner(t, spinner.Line, s.Spinner)
})
t.Run("WithSpinner", func(t *testing.T) {
customSpinner := spinner.Spinner{
Frames: []string{"a", "b", "c", "d"},
FPS: 16,
}
s := spinner.New(spinner.WithSpinner(customSpinner))
assertEqualSpinner(t, customSpinner, s.Spinner)
})
tests := map[string]spinner.Spinner{
"Line": spinner.Line,
"Dot": spinner.Dot,
"MiniDot": spinner.MiniDot,
"Jump": spinner.Jump,
"Pulse": spinner.Pulse,
"Points": spinner.Points,
"Globe": spinner.Globe,
"Moon": spinner.Moon,
"Monkey": spinner.Monkey,
}
for name, s := range tests {
t.Run(name, func(t *testing.T) {
assertEqualSpinner(t, spinner.New(spinner.WithSpinner(s)).Spinner, s)
})
}
}

View File

@@ -205,7 +205,7 @@ func (m *Model) SetValue(s string) {
} else {
m.value = runes
}
if (m.pos == 0 && len(m.value) == 0) || m.pos > len(m.value) {
if m.pos == 0 || m.pos > len(m.value) {
m.setCursor(len(m.value))
}
m.handleOverflow()

View File

@@ -162,9 +162,7 @@ func (m *Model) Start() tea.Cmd {
// Stop pauses the timer. Has no effect if the timer has timed out.
func (m *Model) Stop() tea.Cmd {
return func() tea.Msg {
return m.startStop(false)
}
return m.startStop(false)
}
// Toggle stops the timer if it's running and starts it if it's stopped.