package list import ( "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) var ( appStyle = lipgloss.NewStyle().Padding(1, 2) titleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#FFFDF5")). Background(lipgloss.Color("#25A065")). Padding(0, 1) statusMessageStyle = lipgloss.NewStyle(). Foreground(lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#04B575"}). Render ) type Item struct { title string description string } func NewItem(title string, description string) Item { return Item{title, description} } func (i Item) Title() string { return i.title } func (i Item) Description() string { return i.description } func (i Item) FilterValue() string { return i.title } type listKeyMap struct { toggleSpinner key.Binding toggleTitleBar key.Binding toggleStatusBar key.Binding togglePagination key.Binding toggleHelpMenu key.Binding } func newListKeyMap() *listKeyMap { return &listKeyMap{ toggleSpinner: key.NewBinding( key.WithKeys("s"), key.WithHelp("s", "toggle spinner"), ), toggleTitleBar: key.NewBinding( key.WithKeys("T"), key.WithHelp("T", "toggle title"), ), toggleStatusBar: key.NewBinding( key.WithKeys("S"), key.WithHelp("S", "toggle status"), ), togglePagination: key.NewBinding( key.WithKeys("P"), key.WithHelp("P", "toggle pagination"), ), toggleHelpMenu: key.NewBinding( key.WithKeys("H"), key.WithHelp("H", "toggle help"), ), } } type model struct { list list.Model keys *listKeyMap delegateKeys *delegateKeyMap } func NewList(title string, items []list.Item) model { var ( delegateKeys = newDelegateKeyMap() listKeys = newListKeyMap() ) // Setup list delegate := newItemDelegate(delegateKeys) groceryList := list.New(items, delegate, 0, 0) groceryList.Title = title groceryList.Styles.Title = titleStyle groceryList.AdditionalFullHelpKeys = func() []key.Binding { return []key.Binding{ listKeys.toggleSpinner, listKeys.toggleTitleBar, listKeys.toggleStatusBar, listKeys.togglePagination, listKeys.toggleHelpMenu, } } return model{ list: groceryList, keys: listKeys, delegateKeys: delegateKeys, } } func (m model) Init() tea.Cmd { return tea.EnterAltScreen } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.WindowSizeMsg: h, v := appStyle.GetFrameSize() m.list.SetSize(msg.Width-h, msg.Height-v) case tea.KeyMsg: // Don't match any of the keys below if we're actively filtering. if m.list.FilterState() == list.Filtering { break } switch { case key.Matches(msg, m.keys.toggleSpinner): cmd := m.list.ToggleSpinner() return m, cmd case key.Matches(msg, m.keys.toggleTitleBar): v := !m.list.ShowTitle() m.list.SetShowTitle(v) m.list.SetShowFilter(v) m.list.SetFilteringEnabled(v) return m, nil case key.Matches(msg, m.keys.toggleStatusBar): m.list.SetShowStatusBar(!m.list.ShowStatusBar()) return m, nil case key.Matches(msg, m.keys.togglePagination): m.list.SetShowPagination(!m.list.ShowPagination()) return m, nil case key.Matches(msg, m.keys.toggleHelpMenu): m.list.SetShowHelp(!m.list.ShowHelp()) return m, nil } } // This will also call our delegate's update function. newListModel, cmd := m.list.Update(msg) m.list = newListModel cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } func (m model) View() string { return appStyle.Render(m.list.View()) }