Files
trufflehog/pkg/tui/components/selector/selector.go
renovate[bot]renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Miccah Castorina
af6099665f fix(deps): update module github.com/charmbracelet/bubbletea to v0.25.0 (#2326)
* fix(deps): update module github.com/charmbracelet/bubbletea to v0.25.0

* Remove deprecated and unused mouse events

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Miccah Castorina <[email protected]>
2024-02-11 12:11:46 -08:00

217 lines
5.2 KiB
Go

package selector
import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/trufflesecurity/trufflehog/v3/pkg/tui/common"
)
// Selector is a list of items that can be selected.
type Selector struct {
list.Model
common common.Common
active int
filterState list.FilterState
}
// IdentifiableItem is an item that can be identified by a string. Implements
// list.DefaultItem.
type IdentifiableItem interface {
list.DefaultItem
ID() string
}
// ItemDelegate is a wrapper around list.ItemDelegate.
type ItemDelegate interface {
list.ItemDelegate
}
// SelectMsg is a message that is sent when an item is selected.
type SelectMsg struct{ IdentifiableItem }
// ActiveMsg is a message that is sent when an item is active but not selected.
type ActiveMsg struct{ IdentifiableItem }
// New creates a new selector.
func New(common common.Common, items []IdentifiableItem, delegate ItemDelegate) *Selector {
itms := make([]list.Item, len(items))
for i, item := range items {
itms[i] = item
}
l := list.New(itms, delegate, common.Width, common.Height)
s := &Selector{
Model: l,
common: common,
}
s.SetSize(common.Width, common.Height)
return s
}
// PerPage returns the number of items per page.
func (s *Selector) PerPage() int {
return s.Model.Paginator.PerPage
}
// SetPage sets the current page.
func (s *Selector) SetPage(page int) {
s.Model.Paginator.Page = page
}
// Page returns the current page.
func (s *Selector) Page() int {
return s.Model.Paginator.Page
}
// TotalPages returns the total number of pages.
func (s *Selector) TotalPages() int {
return s.Model.Paginator.TotalPages
}
// Select selects the item at the given index.
func (s *Selector) Select(index int) {
s.Model.Select(index)
}
// SetShowTitle sets the show title flag.
func (s *Selector) SetShowTitle(show bool) {
s.Model.SetShowTitle(show)
}
// SetShowHelp sets the show help flag.
func (s *Selector) SetShowHelp(show bool) {
s.Model.SetShowHelp(show)
}
// SetShowStatusBar sets the show status bar flag.
func (s *Selector) SetShowStatusBar(show bool) {
s.Model.SetShowStatusBar(show)
}
// DisableQuitKeybindings disables the quit keybindings.
func (s *Selector) DisableQuitKeybindings() {
s.Model.DisableQuitKeybindings()
}
// SetShowFilter sets the show filter flag.
func (s *Selector) SetShowFilter(show bool) {
s.Model.SetShowFilter(show)
}
// SetShowPagination sets the show pagination flag.
func (s *Selector) SetShowPagination(show bool) {
s.Model.SetShowPagination(show)
}
// SetFilteringEnabled sets the filtering enabled flag.
func (s *Selector) SetFilteringEnabled(enabled bool) {
s.Model.SetFilteringEnabled(enabled)
}
// SetSize implements common.Component.
func (s *Selector) SetSize(width, height int) {
s.common.SetSize(width, height)
s.Model.SetSize(width, height)
}
// SetItems sets the items in the selector.
func (s *Selector) SetItems(items []IdentifiableItem) tea.Cmd {
its := make([]list.Item, len(items))
for i, item := range items {
its[i] = item
}
return s.Model.SetItems(its)
}
// Index returns the index of the selected item.
func (s *Selector) Index() int {
return s.Model.Index()
}
// Init implements tea.Model.
func (s *Selector) Init() tea.Cmd {
return s.activeCmd
}
// Update implements tea.Model.
func (s *Selector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds := make([]tea.Cmd, 0)
switch msg := msg.(type) {
case tea.KeyMsg:
filterState := s.Model.FilterState()
switch {
case key.Matches(msg, s.common.KeyMap.Help):
if filterState == list.Filtering {
return s, tea.Batch(cmds...)
}
case key.Matches(msg, s.common.KeyMap.Select):
if filterState != list.Filtering {
cmds = append(cmds, s.selectCmd)
}
}
case list.FilterMatchesMsg:
cmds = append(cmds, s.activeFilterCmd)
}
m, cmd := s.Model.Update(msg)
s.Model = m
if cmd != nil {
cmds = append(cmds, cmd)
}
// Track filter state and update active item when filter state changes.
filterState := s.Model.FilterState()
if s.filterState != filterState {
cmds = append(cmds, s.activeFilterCmd)
}
s.filterState = filterState
// Send ActiveMsg when index change.
if s.active != s.Model.Index() {
cmds = append(cmds, s.activeCmd)
}
s.active = s.Model.Index()
return s, tea.Batch(cmds...)
}
// View implements tea.Model.
func (s *Selector) View() string {
return s.Model.View()
}
// SelectItem is a command that selects the currently active item.
func (s *Selector) SelectItem() tea.Msg {
return s.selectCmd()
}
func (s *Selector) selectCmd() tea.Msg {
item := s.Model.SelectedItem()
i, ok := item.(IdentifiableItem)
if !ok {
return SelectMsg{}
}
return SelectMsg{i}
}
func (s *Selector) activeCmd() tea.Msg {
item := s.Model.SelectedItem()
i, ok := item.(IdentifiableItem)
if !ok {
return ActiveMsg{}
}
return ActiveMsg{i}
}
func (s *Selector) activeFilterCmd() tea.Msg {
// Here we use VisibleItems because when list.FilterMatchesMsg is sent,
// VisibleItems is the only way to get the list of filtered items. The list
// bubble should export something like list.FilterMatchesMsg.Items().
items := s.Model.VisibleItems()
if len(items) == 0 {
return nil
}
item := items[0]
i, ok := item.(IdentifiableItem)
if !ok {
return nil
}
return ActiveMsg{i}
}