1package common
2
3import (
4 "strings"
5
6 "charm.land/lipgloss/v2"
7 "github.com/charmbracelet/crush/internal/ui/styles"
8)
9
10// ButtonOpts defines the configuration for a single button
11type ButtonOpts struct {
12 // Text is the button label
13 Text string
14 // UnderlineIndex is the 0-based index of the character to underline (-1 for none)
15 UnderlineIndex int
16 // Selected indicates whether this button is currently selected
17 Selected bool
18 // Padding inner horizontal padding defaults to 2 if this is 0
19 Padding int
20}
21
22// Button creates a button with an underlined character and selection state
23func Button(t *styles.Styles, opts ButtonOpts) string {
24 // Select style based on selection state
25 style := t.ButtonBlur
26 if opts.Selected {
27 style = t.ButtonFocus
28 }
29
30 text := opts.Text
31 if opts.Padding == 0 {
32 opts.Padding = 2
33 }
34
35 // the index is out of bound
36 if opts.UnderlineIndex > -1 && opts.UnderlineIndex > len(text)-1 {
37 opts.UnderlineIndex = -1
38 }
39
40 text = style.Padding(0, opts.Padding).Render(text)
41
42 if opts.UnderlineIndex != -1 {
43 text = lipgloss.StyleRanges(text, lipgloss.NewRange(opts.Padding+opts.UnderlineIndex, opts.Padding+opts.UnderlineIndex+1, style.Underline(true)))
44 }
45
46 return text
47}
48
49// ButtonGroup creates a row of selectable buttons
50// Spacing is the separator between buttons
51// Use " " or similar for horizontal layout
52// Use "\n" for vertical layout
53// Defaults to " " (horizontal)
54func ButtonGroup(t *styles.Styles, buttons []ButtonOpts, spacing string) string {
55 if len(buttons) == 0 {
56 return ""
57 }
58
59 if spacing == "" {
60 spacing = " "
61 }
62
63 parts := make([]string, len(buttons))
64 for i, button := range buttons {
65 parts[i] = Button(t, button)
66 }
67
68 return strings.Join(parts, spacing)
69}