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) (util.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}