huh/layout.go
2025-04-29 20:05:48 -03:00

170 lines
3.6 KiB
Go

package huh
import (
"strings"
"github.com/charmbracelet/lipgloss"
)
// A Layout is responsible for laying out groups in a form.
type Layout interface {
View(f *Form) string
GroupWidth(f *Form, g *Group, w int) int
}
// LayoutDefault is the default layout shows a single group at a time.
var LayoutDefault Layout = &layoutDefault{}
// LayoutStack is a layout stacks all groups on top of each other.
var LayoutStack Layout = &layoutStack{}
// LayoutColumns layout distributes groups in even columns.
func LayoutColumns(columns int) Layout {
return &layoutColumns{columns: columns}
}
// LayoutGrid layout distributes groups in a grid.
func LayoutGrid(rows int, columns int) Layout {
return &layoutGrid{rows: rows, columns: columns}
}
type layoutDefault struct{}
func (l *layoutDefault) View(f *Form) string {
return f.selector.Selected().View()
}
func (l *layoutDefault) GroupWidth(_ *Form, _ *Group, w int) int {
return w
}
type layoutColumns struct {
columns int
}
func (l *layoutColumns) visibleGroups(f *Form) []*Group {
segmentIndex := f.selector.Index() / l.columns
start := segmentIndex * l.columns
end := start + l.columns
total := f.selector.Total()
if end > total {
end = total
}
var groups []*Group
f.selector.Range(func(i int, group *Group) bool {
if i >= start && i < end {
groups = append(groups, group)
return true
}
return true
})
return groups
}
func (l *layoutColumns) View(f *Form) string {
groups := l.visibleGroups(f)
if len(groups) == 0 {
return ""
}
columns := make([]string, 0, len(groups))
for _, group := range groups {
columns = append(columns, group.Content())
}
header := f.selector.Selected().Header()
footer := f.selector.Selected().Footer()
return strings.Join([]string{
header,
lipgloss.JoinHorizontal(lipgloss.Left, columns...),
footer,
}, "\n")
}
func (l *layoutColumns) GroupWidth(_ *Form, _ *Group, w int) int {
return w / l.columns
}
type layoutStack struct{}
func (l *layoutStack) View(f *Form) string {
var columns []string
f.selector.Range(func(_ int, group *Group) bool {
columns = append(columns, group.Content(), "")
return true
})
if footer := f.selector.Selected().Footer(); footer != "" {
columns = append(columns, footer)
}
return strings.Join(columns, "\n")
}
func (l *layoutStack) GroupWidth(_ *Form, _ *Group, w int) int {
return w
}
type layoutGrid struct {
rows, columns int
}
func (l *layoutGrid) visibleGroups(f *Form) [][]*Group {
total := l.rows * l.columns
segmentIndex := f.selector.Index() / total
start := segmentIndex * total
end := start + total
if glen := f.selector.Total(); end > glen {
end = glen
}
var visible []*Group
f.selector.Range(func(i int, group *Group) bool {
if i >= start && i < end {
visible = append(visible, group)
return true
}
return true
})
grid := make([][]*Group, l.rows)
for i := 0; i < l.rows; i++ {
startRow := i * l.columns
endRow := startRow + l.columns
if startRow >= len(visible) {
break
}
if endRow > len(visible) {
endRow = len(visible)
}
grid[i] = visible[startRow:endRow]
}
return grid
}
func (l *layoutGrid) View(f *Form) string {
grid := l.visibleGroups(f)
if len(grid) == 0 {
return ""
}
rows := make([]string, 0, len(grid))
for _, row := range grid {
var columns []string
for _, group := range row {
columns = append(columns, group.Content())
}
rows = append(rows, lipgloss.JoinHorizontal(lipgloss.Left, columns...), "")
}
footer := f.selector.Selected().Footer()
return strings.Join(append(rows, footer), "\n")
}
func (l *layoutGrid) GroupWidth(_ *Form, _ *Group, w int) int {
return w / l.columns
}