border.go

  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}