1package selector
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
17type Item struct {
18 Title string
19 Name string
20 Description string
21 LastUpdate time.Time
22 URL *yankable.Yankable
23}
24
25func (i *Item) Init() tea.Cmd {
26 return nil
27}
28
29func (i *Item) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
30 return i, nil
31}
32
33func (i *Item) View() string {
34 return ""
35}
36
37func (i Item) FilterValue() string { return i.Title }
38
39type ItemDelegate struct {
40 styles *styles.Styles
41}
42
43func (d ItemDelegate) Width() int {
44 width := d.styles.MenuItem.GetHorizontalFrameSize() + d.styles.MenuItem.GetWidth()
45 return width
46}
47func (d ItemDelegate) Height() int {
48 height := d.styles.MenuItem.GetVerticalFrameSize() + d.styles.MenuItem.GetHeight()
49 return height
50}
51func (d ItemDelegate) Spacing() int { return 1 }
52func (d ItemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd {
53 cmds := make([]tea.Cmd, 0)
54 for i, item := range m.VisibleItems() {
55 itm, ok := item.(Item)
56 if !ok {
57 continue
58 }
59 // FIXME check if X & Y are within the item box
60 switch msg := msg.(type) {
61 case tea.MouseMsg:
62 x := msg.X
63 y := msg.Y
64 minX := (i * d.Width())
65 maxX := minX + d.Width()
66 minY := (i * d.Height())
67 maxY := minY + d.Height()
68 // log.Printf("i: %d, x: %d, y: %d", i, x, y)
69 if y < minY || y > maxY || x < minX || x > maxX {
70 continue
71 }
72 }
73 y, cmd := itm.URL.Update(msg)
74 itm.URL = y.(*yankable.Yankable)
75 if cmd != nil {
76 cmds = append(cmds, cmd)
77 }
78 }
79 return tea.Batch(cmds...)
80}
81func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
82 i := listItem.(Item)
83 s := strings.Builder{}
84 style := d.styles.MenuItem.Copy()
85 if index == m.Index() {
86 style = d.styles.SelectedMenuItem.Copy()
87 }
88 style.Width(m.Width() - 2) // FIXME figure out where this "2" comes from
89 titleStr := i.Title
90 updatedStr := fmt.Sprintf(" Updated %s", humanize.Time(i.LastUpdate))
91 updated := d.styles.MenuLastUpdate.
92 Copy().
93 Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(titleStr)).
94 Render(updatedStr)
95 title := lipgloss.NewStyle().
96 Align(lipgloss.Left).
97 Width(m.Width() - style.GetHorizontalFrameSize() - lipgloss.Width(updated)).
98 Render(titleStr)
99
100 s.WriteString(lipgloss.JoinHorizontal(lipgloss.Bottom, title, updated))
101 s.WriteString("\n")
102 s.WriteString(i.Description)
103 s.WriteString("\n\n")
104 s.WriteString(i.URL.View())
105 w.Write([]byte(style.Render(s.String())))
106}