forked from toolshed/abra
		
	
		
			
				
	
	
		
			176 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package lipgloss
 | |
| 
 | |
| import (
 | |
| 	"math"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/charmbracelet/x/ansi"
 | |
| )
 | |
| 
 | |
| // JoinHorizontal is a utility function for horizontally joining two
 | |
| // potentially multi-lined strings along a vertical axis. The first argument is
 | |
| // the position, with 0 being all the way at the top and 1 being all the way
 | |
| // at the bottom.
 | |
| //
 | |
| // If you just want to align to the top, center or bottom you may as well just
 | |
| // use the helper constants Top, Center, and Bottom.
 | |
| //
 | |
| // Example:
 | |
| //
 | |
| //	blockB := "...\n...\n..."
 | |
| //	blockA := "...\n...\n...\n...\n..."
 | |
| //
 | |
| //	// Join 20% from the top
 | |
| //	str := lipgloss.JoinHorizontal(0.2, blockA, blockB)
 | |
| //
 | |
| //	// Join on the top edge
 | |
| //	str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB)
 | |
| func JoinHorizontal(pos Position, strs ...string) string {
 | |
| 	if len(strs) == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	if len(strs) == 1 {
 | |
| 		return strs[0]
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		// Groups of strings broken into multiple lines
 | |
| 		blocks = make([][]string, len(strs))
 | |
| 
 | |
| 		// Max line widths for the above text blocks
 | |
| 		maxWidths = make([]int, len(strs))
 | |
| 
 | |
| 		// Height of the tallest block
 | |
| 		maxHeight int
 | |
| 	)
 | |
| 
 | |
| 	// Break text blocks into lines and get max widths for each text block
 | |
| 	for i, str := range strs {
 | |
| 		blocks[i], maxWidths[i] = getLines(str)
 | |
| 		if len(blocks[i]) > maxHeight {
 | |
| 			maxHeight = len(blocks[i])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Add extra lines to make each side the same height
 | |
| 	for i := range blocks {
 | |
| 		if len(blocks[i]) >= maxHeight {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		extraLines := make([]string, maxHeight-len(blocks[i]))
 | |
| 
 | |
| 		switch pos { //nolint:exhaustive
 | |
| 		case Top:
 | |
| 			blocks[i] = append(blocks[i], extraLines...)
 | |
| 
 | |
| 		case Bottom:
 | |
| 			blocks[i] = append(extraLines, blocks[i]...)
 | |
| 
 | |
| 		default: // Somewhere in the middle
 | |
| 			n := len(extraLines)
 | |
| 			split := int(math.Round(float64(n) * pos.value()))
 | |
| 			top := n - split
 | |
| 			bottom := n - top
 | |
| 
 | |
| 			blocks[i] = append(extraLines[top:], blocks[i]...)
 | |
| 			blocks[i] = append(blocks[i], extraLines[bottom:]...)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Merge lines
 | |
| 	var b strings.Builder
 | |
| 	for i := range blocks[0] { // remember, all blocks have the same number of members now
 | |
| 		for j, block := range blocks {
 | |
| 			b.WriteString(block[i])
 | |
| 
 | |
| 			// Also make lines the same length
 | |
| 			b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.StringWidth(block[i])))
 | |
| 		}
 | |
| 		if i < len(blocks[0])-1 {
 | |
| 			b.WriteRune('\n')
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return b.String()
 | |
| }
 | |
| 
 | |
| // JoinVertical is a utility function for vertically joining two potentially
 | |
| // multi-lined strings along a horizontal axis. The first argument is the
 | |
| // position, with 0 being all the way to the left and 1 being all the way to
 | |
| // the right.
 | |
| //
 | |
| // If you just want to align to the left, right or center you may as well just
 | |
| // use the helper constants Left, Center, and Right.
 | |
| //
 | |
| // Example:
 | |
| //
 | |
| //	blockB := "...\n...\n..."
 | |
| //	blockA := "...\n...\n...\n...\n..."
 | |
| //
 | |
| //	// Join 20% from the top
 | |
| //	str := lipgloss.JoinVertical(0.2, blockA, blockB)
 | |
| //
 | |
| //	// Join on the right edge
 | |
| //	str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB)
 | |
| func JoinVertical(pos Position, strs ...string) string {
 | |
| 	if len(strs) == 0 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	if len(strs) == 1 {
 | |
| 		return strs[0]
 | |
| 	}
 | |
| 
 | |
| 	var (
 | |
| 		blocks   = make([][]string, len(strs))
 | |
| 		maxWidth int
 | |
| 	)
 | |
| 
 | |
| 	for i := range strs {
 | |
| 		var w int
 | |
| 		blocks[i], w = getLines(strs[i])
 | |
| 		if w > maxWidth {
 | |
| 			maxWidth = w
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var b strings.Builder
 | |
| 	for i, block := range blocks {
 | |
| 		for j, line := range block {
 | |
| 			w := maxWidth - ansi.StringWidth(line)
 | |
| 
 | |
| 			switch pos { //nolint:exhaustive
 | |
| 			case Left:
 | |
| 				b.WriteString(line)
 | |
| 				b.WriteString(strings.Repeat(" ", w))
 | |
| 
 | |
| 			case Right:
 | |
| 				b.WriteString(strings.Repeat(" ", w))
 | |
| 				b.WriteString(line)
 | |
| 
 | |
| 			default: // Somewhere in the middle
 | |
| 				if w < 1 {
 | |
| 					b.WriteString(line)
 | |
| 					break
 | |
| 				}
 | |
| 
 | |
| 				split := int(math.Round(float64(w) * pos.value()))
 | |
| 				right := w - split
 | |
| 				left := w - right
 | |
| 
 | |
| 				b.WriteString(strings.Repeat(" ", left))
 | |
| 				b.WriteString(line)
 | |
| 				b.WriteString(strings.Repeat(" ", right))
 | |
| 			}
 | |
| 
 | |
| 			// Write a newline as long as we're not on the last line of the
 | |
| 			// last block.
 | |
| 			if !(i == len(blocks)-1 && j == len(block)-1) {
 | |
| 				b.WriteRune('\n')
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return b.String()
 | |
| }
 |