1package list
2
3import (
4 "slices"
5
6 tea "github.com/charmbracelet/bubbletea/v2"
7 "github.com/charmbracelet/crush/internal/csync"
8 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
9 "github.com/charmbracelet/crush/internal/tui/util"
10)
11
12type Group[T Item] struct {
13 Section ItemSection
14 Items []T
15}
16type GroupedList[T Item] interface {
17 util.Model
18 layout.Sizeable
19 Items() []Item
20 Groups() []Group[T]
21 SetGroups([]Group[T]) tea.Cmd
22 MoveUp(int) tea.Cmd
23 MoveDown(int) tea.Cmd
24 GoToTop() tea.Cmd
25 GoToBottom() tea.Cmd
26 SelectItemAbove() tea.Cmd
27 SelectItemBelow() tea.Cmd
28 SetSelected(string) tea.Cmd
29 SelectedItem() *T
30}
31type groupedList[T Item] struct {
32 *list[Item]
33 groups []Group[T]
34}
35
36func NewGroupedList[T Item](groups []Group[T], opts ...ListOption) GroupedList[T] {
37 list := &list[Item]{
38 confOptions: &confOptions{
39 direction: DirectionForward,
40 keyMap: DefaultKeyMap(),
41 focused: true,
42 },
43 items: csync.NewSlice[Item](),
44 indexMap: csync.NewMap[string, int](),
45 renderedItems: csync.NewMap[string, renderedItem](),
46 }
47 for _, opt := range opts {
48 opt(list.confOptions)
49 }
50
51 return &groupedList[T]{
52 list: list,
53 }
54}
55
56func (g *groupedList[T]) Init() tea.Cmd {
57 g.convertItems()
58 return g.render()
59}
60
61func (l *groupedList[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
62 u, cmd := l.list.Update(msg)
63 l.list = u.(*list[Item])
64 return l, cmd
65}
66
67func (g *groupedList[T]) SelectedItem() *T {
68 item := g.list.SelectedItem()
69 if item == nil {
70 return nil
71 }
72 dRef := *item
73 c, ok := any(dRef).(T)
74 if !ok {
75 return nil
76 }
77 return &c
78}
79
80func (g *groupedList[T]) convertItems() {
81 var items []Item
82 for _, g := range g.groups {
83 items = append(items, g.Section)
84 for _, g := range g.Items {
85 items = append(items, g)
86 }
87 }
88 g.items.SetSlice(items)
89}
90
91func (g *groupedList[T]) SetGroups(groups []Group[T]) tea.Cmd {
92 g.groups = groups
93 g.convertItems()
94 return g.SetItems(slices.Collect(g.items.Seq()))
95}
96
97func (g *groupedList[T]) Groups() []Group[T] {
98 return g.groups
99}
100
101func (g *groupedList[T]) Items() []Item {
102 return g.list.Items()
103}