1package layout
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/charmbracelet/lipgloss"
8 "github.com/kujtimiihoxha/termai/internal/tui/styles"
9)
10
11type BorderPosition int
12
13const (
14 TopLeftBorder BorderPosition = iota
15 TopMiddleBorder
16 TopRightBorder
17 BottomLeftBorder
18 BottomMiddleBorder
19 BottomRightBorder
20)
21
22var (
23 ActiveBorder = styles.Blue
24 InactivePreviewBorder = styles.Grey
25)
26
27func Borderize(content string, active bool, embeddedText map[BorderPosition]string, activeColor lipgloss.TerminalColor) string {
28 if embeddedText == nil {
29 embeddedText = make(map[BorderPosition]string)
30 }
31 if activeColor == nil {
32 activeColor = ActiveBorder
33 }
34 var (
35 thickness = map[bool]lipgloss.Border{
36 true: lipgloss.Border(lipgloss.ThickBorder()),
37 false: lipgloss.Border(lipgloss.NormalBorder()),
38 }
39 color = map[bool]lipgloss.TerminalColor{
40 true: activeColor,
41 false: InactivePreviewBorder,
42 }
43 border = thickness[active]
44 style = lipgloss.NewStyle().Foreground(color[active])
45 width = lipgloss.Width(content)
46 )
47
48 encloseInSquareBrackets := func(text string) string {
49 if text != "" {
50 return fmt.Sprintf("%s%s%s",
51 style.Render(border.TopRight),
52 text,
53 style.Render(border.TopLeft),
54 )
55 }
56 return text
57 }
58 buildHorizontalBorder := func(leftText, middleText, rightText, leftCorner, inbetween, rightCorner string) string {
59 leftText = encloseInSquareBrackets(leftText)
60 middleText = encloseInSquareBrackets(middleText)
61 rightText = encloseInSquareBrackets(rightText)
62 // Calculate length of border between embedded texts
63 remaining := max(0, width-lipgloss.Width(leftText)-lipgloss.Width(middleText)-lipgloss.Width(rightText))
64 leftBorderLen := max(0, (width/2)-lipgloss.Width(leftText)-(lipgloss.Width(middleText)/2))
65 rightBorderLen := max(0, remaining-leftBorderLen)
66 // Then construct border string
67 s := leftText +
68 style.Render(strings.Repeat(inbetween, leftBorderLen)) +
69 middleText +
70 style.Render(strings.Repeat(inbetween, rightBorderLen)) +
71 rightText
72 // Make it fit in the space available between the two corners.
73 s = lipgloss.NewStyle().
74 Inline(true).
75 MaxWidth(width).
76 Render(s)
77 // Add the corners
78 return style.Render(leftCorner) + s + style.Render(rightCorner)
79 }
80 // Stack top border, content and horizontal borders, and bottom border.
81 return strings.Join([]string{
82 buildHorizontalBorder(
83 embeddedText[TopLeftBorder],
84 embeddedText[TopMiddleBorder],
85 embeddedText[TopRightBorder],
86 border.TopLeft,
87 border.Top,
88 border.TopRight,
89 ),
90 lipgloss.NewStyle().
91 BorderForeground(color[active]).
92 Border(border, false, true, false, true).Render(content),
93 buildHorizontalBorder(
94 embeddedText[BottomLeftBorder],
95 embeddedText[BottomMiddleBorder],
96 embeddedText[BottomRightBorder],
97 border.BottomLeft,
98 border.Bottom,
99 border.BottomRight,
100 ),
101 }, "\n")
102}