item.go

  1package selection
  2
  3import (
  4	"fmt"
  5	"io"
  6	"strings"
  7	"time"
  8
  9	"github.com/charmbracelet/bubbles/list"
 10	tea "github.com/charmbracelet/bubbletea"
 11	"github.com/charmbracelet/lipgloss"
 12	"github.com/charmbracelet/soft-serve/ui/components/yankable"
 13	"github.com/charmbracelet/soft-serve/ui/styles"
 14	"github.com/dustin/go-humanize"
 15)
 16
 17// Item represents a single item in the selector.
 18type Item struct {
 19	Title       string
 20	Name        string
 21	Description string
 22	LastUpdate  time.Time
 23	URL         *yankable.Yankable
 24}
 25
 26// ID implements selector.IdentifiableItem.
 27func (i Item) ID() string {
 28	return i.Name
 29}
 30
 31// FilterValue implements list.Item.
 32func (i Item) FilterValue() string { return i.Title }
 33
 34// ItemDelegate is the delegate for the item.
 35type ItemDelegate struct {
 36	styles    *styles.Styles
 37	activeBox *box
 38}
 39
 40// Width returns the item width.
 41func (d ItemDelegate) Width() int {
 42	width := d.styles.MenuItem.GetHorizontalFrameSize() + d.styles.MenuItem.GetWidth()
 43	return width
 44}
 45
 46// Height returns the item height. Implements list.ItemDelegate.
 47func (d ItemDelegate) Height() int {
 48	height := d.styles.MenuItem.GetVerticalFrameSize() + d.styles.MenuItem.GetHeight()
 49	return height
 50}
 51
 52// Spacing returns the spacing between items. Implements list.ItemDelegate.
 53func (d ItemDelegate) Spacing() int { return 1 }
 54
 55// Update implements list.ItemDelegate.
 56func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
 57	cmds := make([]tea.Cmd, 0)
 58	// if d.activeBox == nil || *d.activeBox != selectorBox {
 59	// 	return nil
 60	// }
 61	for i, item := range m.VisibleItems() {
 62		itm, ok := item.(Item)
 63		if !ok {
 64			continue
 65		}
 66		// FIXME check if X & Y are within the item box
 67		switch msg := msg.(type) {
 68		case tea.MouseMsg:
 69			// x := msg.X
 70			y := msg.Y
 71			// minX := (i * d.Width())
 72			// maxX := minX + d.Width()
 73			minY := (i * d.Height())
 74			maxY := minY + d.Height()
 75			// log.Printf("i: %d, x: %d, y: %d", i, x, y)
 76			if y < minY || y > maxY {
 77				continue
 78			}
 79		}
 80		y, cmd := itm.URL.Update(msg)
 81		itm.URL = y.(*yankable.Yankable)
 82		if cmd != nil {
 83			cmds = append(cmds, cmd)
 84		}
 85	}
 86	return tea.Batch(cmds...)
 87}
 88
 89// Render implements list.ItemDelegate.
 90func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
 91	i := listItem.(Item)
 92	s := strings.Builder{}
 93	style := d.styles.MenuItem.Copy()
 94	if index == m.Index() {
 95		style = style.BorderForeground(d.styles.ActiveBorderColor)
 96		if d.activeBox != nil && *d.activeBox == readmeBox {
 97			// TODO make this into its own color
 98			style = style.BorderForeground(lipgloss.Color("15"))
 99		}
100	}
101	titleStr := i.Title
102	updatedStr := fmt.Sprintf(" Updated %s", humanize.Time(i.LastUpdate))
103	updated := d.styles.MenuLastUpdate.
104		Copy().
105		Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(titleStr)).
106		Render(updatedStr)
107	title := lipgloss.NewStyle().
108		Align(lipgloss.Left).
109		Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(updated)).
110		Render(titleStr)
111
112	s.WriteString(lipgloss.JoinHorizontal(lipgloss.Bottom, title, updated))
113	s.WriteString("\n")
114	s.WriteString(i.Description)
115	s.WriteString("\n\n")
116	s.WriteString(i.URL.View())
117	w.Write([]byte(style.Render(s.String())))
118}