From 45af81d4c62af19e1f799e6f2defcd3e599cbca7 Mon Sep 17 00:00:00 2001 From: Shay Sapozhnikov <45404072+ShaySapozhnikov@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:27:28 -0500 Subject: [PATCH] feat(dropdown): add single-choice dropdown bubble component Introduces a focusable dropdown with keyboard navigation, selection messages, runtime option updates, and a demo example with VHS recording. Co-authored-by: Cursor --- dropdown/demo.gif | Bin 0 -> 78376 bytes dropdown/demo.tape | 47 ++++ dropdown/dropdown.go | 460 ++++++++++++++++++++++++++++++++++++++ dropdown/dropdown_test.go | 394 ++++++++++++++++++++++++++++++++ examples/dropdown/main.go | 150 +++++++++++++ 5 files changed, 1051 insertions(+) create mode 100644 dropdown/demo.gif create mode 100644 dropdown/demo.tape create mode 100644 dropdown/dropdown.go create mode 100644 dropdown/dropdown_test.go create mode 100644 examples/dropdown/main.go diff --git a/dropdown/demo.gif b/dropdown/demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..07ad93ebb46a79d08df34f52077a760786c7a829 GIT binary patch literal 78376 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�kqRmk7D%>uiO4JI&#+_Dx%s6} zT-uC*v8jb$DnPH`2Mg&->apdzGnCiy1dD9_fB@>Azdd zv}zlUSzfc%87vv=cD(p__4PZBPfRXtLh4>AcNMG6N>?8LYkZjJ6YIn26~`jCUuisg yRqeh#w-uXs=5LA6>(1<_mOtcEieL|{mu~Zra1m(L2;fnkv_i>G>z}XygEatnB~?cN literal 0 HcmV?d00001 diff --git a/dropdown/demo.tape b/dropdown/demo.tape new file mode 100644 index 0000000..0724e13 --- /dev/null +++ b/dropdown/demo.tape @@ -0,0 +1,47 @@ +Output dropdown.gif + +Set Shell "bash" +Set FontSize 14 +Set Width 640 +Set Height 420 +Set Theme "Catppuccin Mocha" +Set Padding 20 + +Type "go run ./examples/dropdown" +Enter +Sleep 1s + +# Open the first dropdown +Type " " +Sleep 500ms + +# Navigate down twice and select "Bubbles" +Type "jj" +Sleep 300ms +Type " " +Sleep 500ms + +# Reload options at runtime +Type "r" +Sleep 500ms + +# Open again and pick "Rust" +Type " " +Sleep 300ms +Type "j" +Sleep 300ms +Enter +Sleep 500ms + +# Tab to the disabled dropdown (no effect on input) +Type " " +Sleep 300ms +Tab +Sleep 500ms + +# Tab to empty dropdown +Tab +Sleep 500ms + +# Quit +Type "q" diff --git a/dropdown/dropdown.go b/dropdown/dropdown.go new file mode 100644 index 0000000..580aafd --- /dev/null +++ b/dropdown/dropdown.go @@ -0,0 +1,460 @@ +// Package dropdown provides a single-choice dropdown component for Bubble Tea +// applications. It follows the same Model–Update–View pattern and conventions +// as the other Bubbles components (table, paginator, spinner, etc.). +package dropdown + +import ( + "strings" + + "charm.land/bubbles/v2/key" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" + "github.com/charmbracelet/x/ansi" +) + +const ( + defaultMaxVisible = 6 + defaultWidth = 20 +) + +// KeyMap is the key bindings for different actions within the dropdown. +// It satisfies the help.KeyMap interface so it integrates with the help +// component out of the box. +type KeyMap struct { + // Open opens the dropdown when it is collapsed. + Open key.Binding + // Close closes the dropdown without changing the current selection. + Close key.Binding + // Confirm confirms the highlighted option and collapses the dropdown. + Confirm key.Binding + // Up moves the highlight cursor up. + Up key.Binding + // Down moves the highlight cursor down. + Down key.Binding +} + +// ShortHelp implements help.KeyMap. +func (km KeyMap) ShortHelp() []key.Binding { + return []key.Binding{km.Up, km.Down, km.Confirm, km.Close} +} + +// FullHelp implements help.KeyMap. +func (km KeyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + {km.Up, km.Down}, + {km.Open, km.Confirm, km.Close}, + } +} + +// DefaultKeyMap returns the default set of key bindings for the dropdown. +func DefaultKeyMap() KeyMap { + return KeyMap{ + Open: key.NewBinding( + key.WithKeys("enter", "space"), + key.WithHelp("enter/space", "open"), + ), + Close: key.NewBinding( + key.WithKeys("esc"), + key.WithHelp("esc", "close"), + ), + Confirm: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + Up: key.NewBinding( + key.WithKeys("up", "k"), + key.WithHelp("↑/k", "up"), + ), + Down: key.NewBinding( + key.WithKeys("down", "j"), + key.WithHelp("↓/j", "down"), + ), + } +} + +// Styles contains style definitions for this dropdown component. By default, +// these values are generated by DefaultStyles. +type Styles struct { + // FocusedHeader is the style for the collapsed header when the component + // has keyboard focus. + FocusedHeader lipgloss.Style + // BlurredHeader is the style for the collapsed header when the component + // does not have keyboard focus. + BlurredHeader lipgloss.Style + // DisabledHeader is the style for the header when the component is disabled. + DisabledHeader lipgloss.Style + + // SelectedOption is the style for the currently highlighted option in the + // expanded list. + SelectedOption lipgloss.Style + // NormalOption is the style for options that are not highlighted. + NormalOption lipgloss.Style + + // OpenIndicator is the decorator appended to the header when expanded. + OpenIndicator lipgloss.Style + // ClosedIndicator is the decorator appended to the header when collapsed. + ClosedIndicator lipgloss.Style +} + +// DefaultStyles returns a set of default style definitions for this dropdown +// component. +func DefaultStyles() Styles { + return Styles{ + FocusedHeader: lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("212")). + Padding(0, 1), + + BlurredHeader: lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("240")). + Foreground(lipgloss.Color("240")). + Padding(0, 1), + + DisabledHeader: lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("238")). + Foreground(lipgloss.Color("238")). + Padding(0, 1), + + SelectedOption: lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("212")). + PaddingLeft(1). + Border(lipgloss.NormalBorder(), false, false, false, true). + BorderForeground(lipgloss.Color("212")), + + NormalOption: lipgloss.NewStyle(). + PaddingLeft(2), //nolint:mnd + + OpenIndicator: lipgloss.NewStyle().Foreground(lipgloss.Color("212")).SetString("▼"), + ClosedIndicator: lipgloss.NewStyle().Foreground(lipgloss.Color("240")).SetString("▶"), + } +} + +// Option represents a single selectable item in the dropdown list. +type Option struct { + // Label is the text displayed in the header and in the expanded list. + Label string + // Value is the semantic value associated with this option. It may differ + // from Label (e.g. "en" vs "English"). + Value string +} + +// SelectMsg is emitted when the user confirms a selection. Parent models +// should type-switch on this in their own Update to react to a completed +// selection. +type SelectMsg struct { + Option Option + Index int +} + +// CloseMsg is emitted when the user closes the dropdown without confirming a +// new selection (e.g. by pressing Escape). +type CloseMsg struct{} + +// Model is the Bubble Tea model for the dropdown component. +type Model struct { + // KeyMap encodes the keybindings recognized by the dropdown. + KeyMap KeyMap + + // Styles holds all visual style definitions for the dropdown. + Styles Styles + + // Placeholder is the text shown in the header when no option is selected. + Placeholder string + + // MaxVisible is the maximum number of options shown at once when expanded. + // Options beyond this count are accessed by scrolling. Defaults to 6. + MaxVisible int + + // Disabled prevents the dropdown from receiving keyboard input and renders + // it with DisabledHeader style. + Disabled bool + + options []Option + cursor int // index of the currently highlighted option + selected int // index of the committed selection; -1 means none + open bool + focus bool + scrollOffset int // index of the first option in the visible window + width int // inner content width for header label and option rows +} + +// New creates a new model with default settings. Use the With* option +// functions to configure it, for example: +// +// dd := dropdown.New( +// dropdown.WithOptions(opts...), +// dropdown.WithWidth(24), +// ) +func New(o ...OptionFunc) Model { + m := Model{ + KeyMap: DefaultKeyMap(), + Styles: DefaultStyles(), + Placeholder: "Select…", + MaxVisible: defaultMaxVisible, + selected: -1, + width: defaultWidth, + } + for _, opt := range o { + opt(&m) + } + return m +} + +// OptionFunc is used to set options in New. For example: +// +// dd := dropdown.New(dropdown.WithWidth(30)) +type OptionFunc func(*Model) + +// WithOptions sets the initial option list. +func WithOptions(opts ...Option) OptionFunc { + return func(m *Model) { + m.options = opts + } +} + +// WithWidth sets the inner content width used for rendering. +func WithWidth(w int) OptionFunc { + return func(m *Model) { + m.width = w + } +} + +// WithPlaceholder sets the placeholder text shown when nothing is selected. +func WithPlaceholder(s string) OptionFunc { + return func(m *Model) { + m.Placeholder = s + } +} + +// WithMaxVisible sets the maximum number of options visible at once when +// the dropdown is expanded. +func WithMaxVisible(n int) OptionFunc { + return func(m *Model) { + m.MaxVisible = n + } +} + +// WithStyles sets the styles. +func WithStyles(s Styles) OptionFunc { + return func(m *Model) { + m.Styles = s + } +} + +// WithKeyMap sets the key map. +func WithKeyMap(km KeyMap) OptionFunc { + return func(m *Model) { + m.KeyMap = km + } +} + +// SetStyles sets the styles after construction. +func (m *Model) SetStyles(s Styles) { + m.Styles = s +} + +// SetOptions replaces the current option list and resets the selection, +// cursor, and scroll offset. +func (m *Model) SetOptions(opts []Option) { + m.options = opts + m.selected = -1 + m.cursor = 0 + m.scrollOffset = 0 +} + +// Options returns a copy of the current option list. +func (m Model) Options() []Option { + out := make([]Option, len(m.options)) + copy(out, m.options) + return out +} + +// SelectedItem returns the currently selected option and true, or an empty +// Option and false when nothing has been selected. +func (m Model) SelectedItem() (Option, bool) { + if m.selected < 0 || m.selected >= len(m.options) { + return Option{}, false + } + return m.options[m.selected], true +} + +// SelectedIndex returns the index of the selected option, or -1 if nothing +// has been selected. +func (m Model) SelectedIndex() int { + return m.selected +} + +// IsOpen returns true when the dropdown is expanded. +func (m Model) IsOpen() bool { + return m.open +} + +// Width returns the inner content width. +func (m Model) Width() int { + return m.width +} + +// SetWidth sets the inner content width. +func (m *Model) SetWidth(w int) { + m.width = w +} + +// Focus grants keyboard focus to the dropdown. It returns nil to match the +// tea.Cmd return type convention used by textinput and other components. +func (m *Model) Focus() tea.Cmd { + m.focus = true + return nil +} + +// Blur removes keyboard focus. The dropdown is also closed so that an +// abandoned expanded list is not left on screen. +func (m *Model) Blur() { + m.focus = false + m.open = false +} + +// Focused returns the current focus state. +func (m Model) Focused() bool { + return m.focus +} + +// Update is the Bubble Tea update loop. +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + if !m.focus || m.Disabled { + return m, nil + } + + keyMsg, ok := msg.(tea.KeyPressMsg) + if !ok { + return m, nil + } + + if !m.open { + if key.Matches(keyMsg, m.KeyMap.Open) && len(m.options) > 0 { + m.open = true + // Start the cursor on the already-selected item when reopening. + if m.selected >= 0 { + m.cursor = m.selected + m.clampScrollOffset() + } + } + return m, nil + } + + switch { + case key.Matches(keyMsg, m.KeyMap.Close): + m.open = false + return m, func() tea.Msg { return CloseMsg{} } + + case key.Matches(keyMsg, m.KeyMap.Confirm): + m.selected = m.cursor + m.open = false + sel, idx := m.options[m.selected], m.selected + return m, func() tea.Msg { return SelectMsg{Option: sel, Index: idx} } + + case key.Matches(keyMsg, m.KeyMap.Up): + if m.cursor > 0 { + m.cursor-- + m.clampScrollOffset() + } + + case key.Matches(keyMsg, m.KeyMap.Down): + if m.cursor < len(m.options)-1 { + m.cursor++ + m.clampScrollOffset() + } + } + + return m, nil +} + +// clampScrollOffset keeps cursor visible within the MaxVisible window. +func (m *Model) clampScrollOffset() { + maxVis := m.MaxVisible + if maxVis <= 0 { + maxVis = defaultMaxVisible + } + if m.cursor < m.scrollOffset { + m.scrollOffset = m.cursor + } + if m.cursor >= m.scrollOffset+maxVis { + m.scrollOffset = m.cursor - maxVis + 1 + } +} + +// View renders the dropdown. +func (m Model) View() string { + var sb strings.Builder + + // ── Header ──────────────────────────────────────────────────────────────── + indicatorStr := m.indicatorString() + // Keep one space between the label and the indicator. + availForLabel := m.width - ansi.StringWidth(indicatorStr) - 1 + if availForLabel < 0 { + availForLabel = 0 + } + label := m.headerLabel() + label = ansi.Truncate(label, availForLabel, "…") + // Pad so the header width stays fixed regardless of label length. + label += strings.Repeat(" ", max(0, availForLabel-ansi.StringWidth(label))) + + sb.WriteString(m.headerStyle().Render(label + " " + indicatorStr)) + + // ── Option list ─────────────────────────────────────────────────────────── + if !m.open || len(m.options) == 0 { + return sb.String() + } + + sb.WriteRune('\n') + + maxVis := m.MaxVisible + if maxVis <= 0 { + maxVis = defaultMaxVisible + } + end := m.scrollOffset + maxVis + if end > len(m.options) { + end = len(m.options) + } + + for i := m.scrollOffset; i < end; i++ { + optLabel := ansi.Truncate(m.options[i].Label, m.width, "…") + if i == m.cursor { + sb.WriteString(m.Styles.SelectedOption.Render(optLabel)) + } else { + sb.WriteString(m.Styles.NormalOption.Render(optLabel)) + } + if i < end-1 { + sb.WriteRune('\n') + } + } + + return sb.String() +} + +func (m Model) headerLabel() string { + if opt, ok := m.SelectedItem(); ok { + return opt.Label + } + return m.Placeholder +} + +func (m Model) indicatorString() string { + if m.open { + return m.Styles.OpenIndicator.String() + } + return m.Styles.ClosedIndicator.String() +} + +func (m Model) headerStyle() lipgloss.Style { + switch { + case m.Disabled: + return m.Styles.DisabledHeader + case m.focus: + return m.Styles.FocusedHeader + default: + return m.Styles.BlurredHeader + } +} diff --git a/dropdown/dropdown_test.go b/dropdown/dropdown_test.go new file mode 100644 index 0000000..0ca1215 --- /dev/null +++ b/dropdown/dropdown_test.go @@ -0,0 +1,394 @@ +package dropdown + +import ( + "testing" + + tea "charm.land/bubbletea/v2" +) + +// helpers ────────────────────────────────────────────────────────────────── + +func testOptions() []Option { + return []Option{ + {Label: "Alpha", Value: "a"}, + {Label: "Beta", Value: "b"}, + {Label: "Gamma", Value: "c"}, + {Label: "Delta", Value: "d"}, + {Label: "Epsilon", Value: "e"}, + } +} + +// press sends a single key event and returns the updated model plus any +// emitted tea.Msg (by calling the returned Cmd immediately). +func press(m Model, code rune) (Model, tea.Msg) { + updated, cmd := m.Update(tea.KeyPressMsg{Code: code}) + var emitted tea.Msg + if cmd != nil { + emitted = cmd() + } + return updated, emitted +} + +func focused(opts []Option) Model { + m := New(WithOptions(opts...), WithWidth(20)) + m.Focus() //nolint:errcheck + return m +} + +// ── Focus / Blur ─────────────────────────────────────────────────────────── + +func TestFocused(t *testing.T) { + m := New(WithOptions(testOptions()...)) + if m.Focused() { + t.Fatal("expected unfocused on creation") + } + m.Focus() //nolint:errcheck + if !m.Focused() { + t.Fatal("expected focused after Focus()") + } + m.Blur() + if m.Focused() { + t.Fatal("expected unfocused after Blur()") + } +} + +func TestBlurClosesDropdown(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + if !m.open { + t.Fatal("expected open after Enter") + } + m.Blur() + if m.open { + t.Fatal("expected closed after Blur()") + } +} + +// ── Disabled ─────────────────────────────────────────────────────────────── + +func TestDisabledIgnoresInput(t *testing.T) { + m := focused(testOptions()) + m.Disabled = true + m, emitted := press(m, tea.KeyEnter) + if m.open { + t.Error("disabled dropdown should not open") + } + if emitted != nil { + t.Error("disabled dropdown should not emit messages") + } +} + +// ── Empty options ───────────────────────────────────────────────────────── + +func TestEmptyOptionsNoOpen(t *testing.T) { + m := New(WithWidth(20)) + m.Focus() //nolint:errcheck + m, _ = press(m, tea.KeyEnter) + if m.open { + t.Fatal("empty dropdown should not open") + } +} + +func TestEmptyOptionsNoPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("unexpected panic: %v", r) + } + }() + m := New(WithWidth(20)) + m.Focus() //nolint:errcheck + for _, k := range []rune{tea.KeyEnter, tea.KeyDown, tea.KeyUp, tea.KeyEscape} { + m, _ = press(m, k) + } + _ = m.View() +} + +// ── Opening / Closing ───────────────────────────────────────────────────── + +func TestOpenWithEnter(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + if !m.IsOpen() { + t.Fatal("should be open after Enter") + } +} + +func TestOpenWithSpace(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeySpace) + if !m.IsOpen() { + t.Fatal("should be open after Space") + } +} + +func TestCloseWithEscape(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, emitted := press(m, tea.KeyEscape) + if m.IsOpen() { + t.Fatal("should be closed after Escape") + } + if _, ok := emitted.(CloseMsg); !ok { + t.Fatalf("expected CloseMsg, got %T", emitted) + } +} + +func TestEscapeDoesNotChangeSelection(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyDown) + m, _ = press(m, tea.KeyEscape) + if m.SelectedIndex() != -1 { + t.Fatalf("Escape should not commit selection; got index %d", m.SelectedIndex()) + } +} + +// ── Navigation ──────────────────────────────────────────────────────────── + +func TestCursorDownUp(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyDown) + m, _ = press(m, tea.KeyDown) + if m.cursor != 2 { + t.Fatalf("expected cursor=2, got %d", m.cursor) + } + m, _ = press(m, tea.KeyUp) + if m.cursor != 1 { + t.Fatalf("expected cursor=1 after Up, got %d", m.cursor) + } +} + +func TestCursorNoWrapAtTop(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyUp) + if m.cursor != 0 { + t.Fatalf("cursor should stay at 0, got %d", m.cursor) + } +} + +func TestCursorNoWrapAtBottom(t *testing.T) { + opts := testOptions() + m := focused(opts) + m, _ = press(m, tea.KeyEnter) + for i := 0; i < len(opts)-1; i++ { + m, _ = press(m, tea.KeyDown) + } + last := m.cursor + m, _ = press(m, tea.KeyDown) + if m.cursor != last { + t.Fatalf("cursor should not go past last option; got %d", m.cursor) + } +} + +func TestVimKeys(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = m.Update(tea.KeyPressMsg{Text: "j"}) + if m.cursor != 1 { + t.Fatalf("expected cursor=1 after 'j', got %d", m.cursor) + } + m, _ = m.Update(tea.KeyPressMsg{Text: "k"}) + if m.cursor != 0 { + t.Fatalf("expected cursor=0 after 'k', got %d", m.cursor) + } +} + +// ── Selection (SelectMsg) ───────────────────────────────────────────────── + +func TestSelectEmitsSelectMsg(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) // open + m, _ = press(m, tea.KeyDown) // cursor → 1 "Beta" + m, emitted := press(m, tea.KeyEnter) + + sel, ok := emitted.(SelectMsg) + if !ok { + t.Fatalf("expected SelectMsg, got %T", emitted) + } + if sel.Index != 1 { + t.Errorf("expected index 1, got %d", sel.Index) + } + if sel.Option.Value != "b" { + t.Errorf("expected value 'b', got %q", sel.Option.Value) + } +} + +func TestSelectCommitsSelection(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyDown) + m, _ = press(m, tea.KeyDown) + m, _ = press(m, tea.KeyEnter) + + if m.SelectedIndex() != 2 { + t.Fatalf("expected selected=2, got %d", m.SelectedIndex()) + } + opt, ok := m.SelectedItem() + if !ok { + t.Fatal("SelectedItem should return true after selection") + } + if opt.Label != "Gamma" { + t.Errorf("expected 'Gamma', got %q", opt.Label) + } +} + +func TestSelectClosesDropdown(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyEnter) + if m.IsOpen() { + t.Fatal("dropdown should close after selecting") + } +} + +func TestSelectedItemNoneInitially(t *testing.T) { + m := New(WithOptions(testOptions()...)) + _, ok := m.SelectedItem() + if ok { + t.Fatal("expected no selection initially") + } + if m.SelectedIndex() != -1 { + t.Fatalf("expected SelectedIndex=-1, got %d", m.SelectedIndex()) + } +} + +// ── Scroll offset ───────────────────────────────────────────────────────── + +func TestScrollOffsetAdvancesWithCursor(t *testing.T) { + m := New(WithOptions(testOptions()...), WithWidth(20), WithMaxVisible(3)) + m.Focus() //nolint:errcheck + m, _ = press(m, tea.KeyEnter) + for i := 0; i < 4; i++ { + m, _ = press(m, tea.KeyDown) + } + if m.scrollOffset < 2 { + t.Fatalf("expected scrollOffset>=2 at cursor=4 with MaxVisible=3, got %d", m.scrollOffset) + } + end := m.scrollOffset + m.MaxVisible + if m.cursor < m.scrollOffset || m.cursor >= end { + t.Fatalf("cursor %d outside visible window [%d, %d)", m.cursor, m.scrollOffset, end) + } +} + +func TestScrollOffsetRetreatsWithCursor(t *testing.T) { + opts := testOptions() + m := New(WithOptions(opts...), WithWidth(20), WithMaxVisible(3)) + m.Focus() //nolint:errcheck + m, _ = press(m, tea.KeyEnter) + for i := 0; i < len(opts)-1; i++ { + m, _ = press(m, tea.KeyDown) + } + prevOffset := m.scrollOffset + for i := 0; i < len(opts)-1; i++ { + m, _ = press(m, tea.KeyUp) + } + if m.scrollOffset >= prevOffset { + t.Fatalf("scrollOffset should have decreased (was %d, now %d)", prevOffset, m.scrollOffset) + } + if m.scrollOffset != 0 { + t.Fatalf("expected scrollOffset=0 at top, got %d", m.scrollOffset) + } +} + +// ── SetOptions ──────────────────────────────────────────────────────────── + +func TestSetOptionsResetsState(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyDown) + m, _ = press(m, tea.KeyEnter) + if m.SelectedIndex() == -1 { + t.Fatal("expected a selection before SetOptions") + } + m.SetOptions([]Option{{Label: "X", Value: "x"}}) + if m.SelectedIndex() != -1 { + t.Fatal("SetOptions should reset selection to -1") + } + if m.cursor != 0 { + t.Fatal("SetOptions should reset cursor to 0") + } + if m.scrollOffset != 0 { + t.Fatal("SetOptions should reset scrollOffset to 0") + } +} + +// ── Option funcs ────────────────────────────────────────────────────────── + +func TestWithKeyMap(t *testing.T) { + km := DefaultKeyMap() + m := New(WithKeyMap(km)) + if m.KeyMap.Up.Keys()[0] != km.Up.Keys()[0] { + t.Fatal("WithKeyMap should set the KeyMap") + } +} + +func TestWithStyles(t *testing.T) { + s := DefaultStyles() + m := New(WithStyles(s)) + _ = m.View() // should not panic +} + +// ── View ────────────────────────────────────────────────────────────────── + +func TestViewDoesNotPanic(t *testing.T) { + cases := []struct { + name string + model Model + }{ + {"default", New()}, + {"focused", func() Model { m := New(WithOptions(testOptions()...)); m.Focus(); return m }()}, + {"open", func() Model { + m := New(WithOptions(testOptions()...)) + m.Focus() + m, _ = press(m, tea.KeyEnter) + return m + }()}, + {"disabled", func() Model { + m := New(WithOptions(testOptions()...)) + m.Disabled = true + return m + }()}, + {"empty", New()}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("View() panicked: %v", r) + } + }() + _ = tc.model.View() + }) + } +} + +func TestViewContainsSelectedLabel(t *testing.T) { + m := focused(testOptions()) + m, _ = press(m, tea.KeyEnter) + m, _ = press(m, tea.KeyDown) // cursor → 1 "Beta" + m, _ = press(m, tea.KeyEnter) + view := m.View() + if !containsSubstring(view, "Beta") { + t.Errorf("expected 'Beta' in view, got:\n%s", view) + } +} + +func TestViewContainsPlaceholder(t *testing.T) { + m := New(WithOptions(testOptions()...), WithPlaceholder("Pick one")) + view := m.View() + if !containsSubstring(view, "Pick one") { + t.Errorf("expected placeholder in view, got:\n%s", view) + } +} + +// containsSubstring is an inlined helper to avoid importing strings. +func containsSubstring(s, sub string) bool { + for i := 0; i+len(sub) <= len(s); i++ { + if s[i:i+len(sub)] == sub { + return true + } + } + return false +} diff --git a/examples/dropdown/main.go b/examples/dropdown/main.go new file mode 100644 index 0000000..07b43d0 --- /dev/null +++ b/examples/dropdown/main.go @@ -0,0 +1,150 @@ +// Example program demonstrating the dropdown component. +// +// Controls: +// +// - tab / shift+tab cycle focus between dropdowns +// - ↑/k, ↓/j navigate options (when expanded) +// - enter / space open or confirm selection +// - esc close without selecting +// - r reload options in the first dropdown at runtime +// - q / ctrl+c quit +package main + +import ( + "fmt" + "os" + "strings" + + "charm.land/bubbles/v2/dropdown" + tea "charm.land/bubbletea/v2" +) + +const numDropdowns = 3 + +var charmOpts = []dropdown.Option{ + {Label: "Bubble Tea", Value: "bubbletea"}, + {Label: "Lip Gloss", Value: "lipgloss"}, + {Label: "Bubbles", Value: "bubbles"}, + {Label: "Huh", Value: "huh"}, + {Label: "Wish", Value: "wish"}, +} + +var langOpts = []dropdown.Option{ + {Label: "Go", Value: "go"}, + {Label: "Rust", Value: "rust"}, + {Label: "Zig", Value: "zig"}, + {Label: "C", Value: "c"}, +} + +type model struct { + dropdowns [numDropdowns]dropdown.Model + focusIndex int + lastSelected string +} + +func initialModel() model { + // Dropdown 0: normal, interactive. + dd0 := dropdown.New( + dropdown.WithOptions(charmOpts...), + dropdown.WithWidth(18), + ) + dd0.Focus() //nolint:errcheck + + // Dropdown 1: disabled, with a pre-committed selection. + dd1 := dropdown.New( + dropdown.WithOptions(charmOpts...), + dropdown.WithWidth(18), + ) + // Pre-select "Lip Gloss" (index 1) by running Update with a temporary focus. + dd1.Focus() //nolint:errcheck + dd1, _ = dd1.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) // open + dd1, _ = dd1.Update(tea.KeyPressMsg{Code: tea.KeyDown}) // cursor → 1 + dd1, _ = dd1.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) // confirm + dd1.Blur() + dd1.Disabled = true + + // Dropdown 2: empty options — shows placeholder only, never opens. + dd2 := dropdown.New(dropdown.WithWidth(18)) + + return model{ + dropdowns: [numDropdowns]dropdown.Model{dd0, dd1, dd2}, + focusIndex: 0, + } +} + +func (m model) Init() tea.Cmd { + return nil +} + +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyPressMsg: + switch msg.String() { + case "ctrl+c", "q": + return m, tea.Quit + + case "tab", "shift+tab": + step := 1 + if msg.String() == "shift+tab" { + step = -1 + } + m.dropdowns[m.focusIndex].Blur() + m.focusIndex = (m.focusIndex + step + numDropdowns) % numDropdowns + m.dropdowns[m.focusIndex].Focus() //nolint:errcheck + return m, nil + + case "r": + // Reload options at runtime (only when collapsed). + if !m.dropdowns[0].IsOpen() { + m.dropdowns[0].SetOptions(langOpts) + m.lastSelected = "(options reloaded)" + } + return m, nil + } + + case dropdown.SelectMsg: + m.lastSelected = fmt.Sprintf("selected %q (value: %q, index: %d)", + msg.Option.Label, msg.Option.Value, msg.Index) + return m, nil + + case dropdown.CloseMsg: + m.lastSelected = "(closed without selecting)" + return m, nil + } + + var cmd tea.Cmd + m.dropdowns[m.focusIndex], cmd = m.dropdowns[m.focusIndex].Update(msg) + return m, cmd +} + +func (m model) View() tea.View { + var sb strings.Builder + + sb.WriteString("Dropdown Component Demo\n") + sb.WriteString("───────────────────────────────────────────────\n\n") + + labels := []string{"Normal (tab to focus)", "Disabled", "Empty"} + for i, dd := range m.dropdowns { + sb.WriteString(fmt.Sprintf(" %s\n", labels[i])) + for _, line := range strings.Split(dd.View(), "\n") { + sb.WriteString(" " + line + "\n") + } + sb.WriteRune('\n') + } + + sb.WriteString("───────────────────────────────────────────────\n") + if m.lastSelected != "" { + sb.WriteString(" " + m.lastSelected + "\n") + } + sb.WriteString("\n tab/shift+tab: focus r: reload opts q: quit\n") + + return tea.NewView(sb.String()) +} + +func main() { + p := tea.NewProgram(initialModel()) + if _, err := p.Run(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +}