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		viewCache:     csync.NewMap[string, string](),
 46		itemPositions: nil,
 47	}
 48	for _, opt := range opts {
 49		opt(list.confOptions)
 50	}
 51
 52	return &groupedList[T]{
 53		list: list,
 54	}
 55}
 56
 57func (g *groupedList[T]) Init() tea.Cmd {
 58	g.convertItems()
 59	return g.render()
 60}
 61
 62func (l *groupedList[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 63	u, cmd := l.list.Update(msg)
 64	l.list = u.(*list[Item])
 65	return l, cmd
 66}
 67
 68func (g *groupedList[T]) SelectedItem() *T {
 69	item := g.list.SelectedItem()
 70	if item == nil {
 71		return nil
 72	}
 73	dRef := *item
 74	c, ok := any(dRef).(T)
 75	if !ok {
 76		return nil
 77	}
 78	return &c
 79}
 80
 81func (g *groupedList[T]) convertItems() {
 82	var items []Item
 83	for _, g := range g.groups {
 84		items = append(items, g.Section)
 85		for _, g := range g.Items {
 86			items = append(items, g)
 87		}
 88	}
 89	g.items.SetSlice(items)
 90}
 91
 92func (g *groupedList[T]) SetGroups(groups []Group[T]) tea.Cmd {
 93	g.groups = groups
 94	g.convertItems()
 95	return g.SetItems(slices.Collect(g.items.Seq()))
 96}
 97
 98func (g *groupedList[T]) Groups() []Group[T] {
 99	return g.groups
100}
101
102func (g *groupedList[T]) Items() []Item {
103	return g.list.Items()
104}