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) string {
28 if embeddedText == nil {
29 embeddedText = make(map[BorderPosition]string)
30 }
31 var (
32 thickness = map[bool]lipgloss.Border{
33 true: lipgloss.Border(lipgloss.ThickBorder()),
34 false: lipgloss.Border(lipgloss.NormalBorder()),
35 }
36 color = map[bool]lipgloss.TerminalColor{
37 true: ActiveBorder,
38 false: InactivePreviewBorder,
39 }
40 border = thickness[active]
41 style = lipgloss.NewStyle().Foreground(color[active])
42 width = lipgloss.Width(content)
43 )
44
45 encloseInSquareBrackets := func(text string) string {
46 if text != "" {
47 return fmt.Sprintf("%s%s%s",
48 style.Render(border.TopRight),
49 text,
50 style.Render(border.TopLeft),
51 )
52 }
53 return text
54 }
55 buildHorizontalBorder := func(leftText, middleText, rightText, leftCorner, inbetween, rightCorner string) string {
56 leftText = encloseInSquareBrackets(leftText)
57 middleText = encloseInSquareBrackets(middleText)
58 rightText = encloseInSquareBrackets(rightText)
59 // Calculate length of border between embedded texts
60 remaining := max(0, width-lipgloss.Width(leftText)-lipgloss.Width(middleText)-lipgloss.Width(rightText))
61 leftBorderLen := max(0, (width/2)-lipgloss.Width(leftText)-(lipgloss.Width(middleText)/2))
62 rightBorderLen := max(0, remaining-leftBorderLen)
63 // Then construct border string
64 s := leftText +
65 style.Render(strings.Repeat(inbetween, leftBorderLen)) +
66 middleText +
67 style.Render(strings.Repeat(inbetween, rightBorderLen)) +
68 rightText
69 // Make it fit in the space available between the two corners.
70 s = lipgloss.NewStyle().
71 Inline(true).
72 MaxWidth(width).
73 Render(s)
74 // Add the corners
75 return style.Render(leftCorner) + s + style.Render(rightCorner)
76 }
77 // Stack top border, content and horizontal borders, and bottom border.
78 return strings.Join([]string{
79 buildHorizontalBorder(
80 embeddedText[TopLeftBorder],
81 embeddedText[TopMiddleBorder],
82 embeddedText[TopRightBorder],
83 border.TopLeft,
84 border.Top,
85 border.TopRight,
86 ),
87 lipgloss.NewStyle().
88 BorderForeground(color[active]).
89 Border(border, false, true, false, true).Render(content),
90 buildHorizontalBorder(
91 embeddedText[BottomLeftBorder],
92 embeddedText[BottomMiddleBorder],
93 embeddedText[BottomRightBorder],
94 border.BottomLeft,
95 border.Bottom,
96 border.BottomRight,
97 ),
98 }, "\n")
99}