go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/cli/components/list.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package components 5 6 import ( 7 "fmt" 8 "io" 9 10 "github.com/charmbracelet/bubbles/list" 11 tea "github.com/charmbracelet/bubbletea" 12 "github.com/charmbracelet/lipgloss" 13 ) 14 15 const listHeight = 14 16 17 var ( 18 titleStyle = lipgloss.NewStyle().MarginLeft(2) 19 itemStyle = lipgloss.NewStyle().PaddingLeft(4) 20 selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) 21 paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) 22 helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) 23 quitTextStyle = lipgloss.NewStyle().PaddingLeft(4) 24 ) 25 26 type item string 27 28 func (i item) FilterValue() string { return "" } 29 30 type itemDelegate struct{} 31 32 func (d itemDelegate) Height() int { return 1 } 33 func (d itemDelegate) Spacing() int { return 0 } 34 func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil } 35 func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { 36 i, ok := listItem.(item) 37 if !ok { 38 return 39 } 40 41 str := fmt.Sprintf("%d. %s", index+1, i) 42 43 fn := itemStyle.Render 44 if index == m.Index() { 45 fn = func(strs ...string) string { 46 strs = append([]string{">"}, strs...) 47 return selectedItemStyle.Render(strs...) 48 } 49 } 50 51 fmt.Fprintf(w, fn(str)) 52 } 53 54 type listModel struct { 55 list list.Model 56 items []item 57 choice string 58 quitting bool 59 selectedHandler func(s int) 60 } 61 62 func (m listModel) Init() tea.Cmd { 63 return nil 64 } 65 66 func (m listModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 67 switch msg := msg.(type) { 68 case tea.WindowSizeMsg: 69 m.list.SetWidth(msg.Width) 70 return m, nil 71 72 case tea.KeyMsg: 73 switch keypress := msg.String(); keypress { 74 case "ctrl+c": 75 m.quitting = true 76 m.selectedHandler(-1) 77 return m, tea.Quit 78 79 case "enter": 80 i, ok := m.list.SelectedItem().(item) 81 if ok { 82 m.selectedHandler(m.list.Index()) 83 m.choice = string(i) 84 } 85 return m, tea.Quit 86 } 87 } 88 89 var cmd tea.Cmd 90 m.list, cmd = m.list.Update(msg) 91 return m, cmd 92 } 93 94 func (m listModel) View() string { 95 if m.choice != "" { 96 return quitTextStyle.Render(fmt.Sprintf("selected: %s", m.choice)) 97 } 98 if m.quitting { 99 return quitTextStyle.Render("aborted selection.") 100 } 101 return "\n" + m.list.View() 102 } 103 104 func (m listModel) Selection() string { 105 return m.choice 106 } 107 108 func NewListModel(title string, entries []string, h func(idx int)) *listModel { 109 items := []list.Item{} 110 111 for i := range entries { 112 items = append(items, item(entries[i])) 113 } 114 115 const defaultWidth = 20 116 117 l := list.New(items, itemDelegate{}, defaultWidth, listHeight) 118 l.Title = title 119 l.SetShowStatusBar(false) 120 l.SetFilteringEnabled(false) 121 l.Styles.Title = titleStyle 122 l.Styles.PaginationStyle = paginationStyle 123 l.Styles.HelpStyle = helpStyle 124 125 return &listModel{ 126 list: l, 127 selectedHandler: h, 128 } 129 }