Files
LazyPM/pkg/tui/components/helpbar.go

255 lines
5.8 KiB
Go

package components
import (
"strings"
"charm.land/lipgloss/v2"
"github.com/LazyBachelor/LazyPM/internal/style"
)
type ViewKind int
const (
ViewIssues ViewKind = iota
ViewKanban
)
const (
fullHelpKeyWidth = 10
fullHelpDescWidth = 18
fullHelpColGap = 4
maxFullHelpCols = 4
)
type HelpItem struct {
Key string
Desc string
}
type HelpBarConfig struct {
ShortItems []HelpItem
FullItems []HelpItem
}
type HelpBar struct {
view ViewKind
config HelpBarConfig
showAll bool
width int
}
func NewHelpBar(view ViewKind) HelpBar {
return HelpBar{
view: view,
config: helpBarConfig(view),
}
}
func (h *HelpBar) SetWidth(width int) {
h.width = width
}
func (h HelpBar) View() string {
if h.width == 0 {
return ""
}
if h.showAll {
return h.fullHelp()
}
return h.shortHelp()
}
func (h HelpBar) shortHelp() string {
items := make([]string, 0, len(h.config.ShortItems))
for _, item := range h.config.ShortItems {
items = append(
items,
style.HighlightKey(item.Key)+item.Desc+" ",
)
}
content := lipgloss.JoinHorizontal(lipgloss.Left, items...)
return lipgloss.NewStyle().
Border(lipgloss.Border{Top: "─"}, true, false, false, false).
BorderForeground(style.SecondaryBorder).
Padding(0, 1).
Width(h.width).
Render(content)
}
func (h HelpBar) fullHelp() string {
if len(h.config.FullItems) == 0 {
return ""
}
keyStyle := lipgloss.NewStyle().Width(fullHelpKeyWidth).Align(lipgloss.Right)
descStyle := lipgloss.NewStyle().Width(fullHelpDescWidth)
cellStyle := lipgloss.NewStyle().Width(fullHelpKeyWidth + 1 + fullHelpDescWidth)
renderItem := func(item HelpItem) string {
return cellStyle.Render(
keyStyle.Render(style.HighlightKey(item.Key)) + " " + descStyle.Render(item.Desc),
)
}
innerWidth := max(h.width-2, 1)
cellWidth := fullHelpKeyWidth + 1 + fullHelpDescWidth
cols := fitHelpColumns(innerWidth, cellWidth, fullHelpColGap, maxFullHelpCols)
rows := (len(h.config.FullItems) + cols - 1) / cols
gap := strings.Repeat(" ", fullHelpColGap)
var result []string
for r := range rows {
var cells []string
for c := range cols {
if idx := r + c*rows; idx < len(h.config.FullItems) {
cells = append(cells, renderItem(h.config.FullItems[idx]))
}
}
result = append(result, joinHorizontalWithGap(cells, gap))
}
content := lipgloss.JoinVertical(lipgloss.Left, result...)
return lipgloss.NewStyle().
Border(lipgloss.Border{Top: "─"}, true, false, false, false).
BorderForeground(style.SecondaryBorder).
Padding(0, 1).
Width(h.width).
Render(content)
}
func (h HelpBar) Height() int {
if h.width == 0 {
return 0
}
return lipgloss.Height(h.View())
}
func (h HelpBar) IsExpanded() bool {
return h.showAll
}
func (h *HelpBar) ToggleHelp() {
h.showAll = !h.showAll
}
func fitHelpColumns(
availableWidth int,
cellWidth int,
gapWidth int,
maxCols int,
) int {
for cols := maxCols; cols >= 1; cols-- {
neededWidth := cols*cellWidth + (cols-1)*gapWidth
if neededWidth <= availableWidth {
return cols
}
}
return 1
}
func joinHorizontalWithGap(cells []string, gap string) string {
if len(cells) == 0 {
return ""
}
parts := make([]string, 0, len(cells)*2-1)
for i, cell := range cells {
if i > 0 {
parts = append(parts, gap)
}
parts = append(parts, cell)
}
return lipgloss.JoinHorizontal(lipgloss.Left, parts...)
}
func helpBarConfig(view ViewKind) HelpBarConfig {
switch view {
case ViewIssues:
return HelpBarConfig{
ShortItems: []HelpItem{
{Key: "v", Desc: "kanban"},
{Key: "↑/k", Desc: "up"},
{Key: "↓/j", Desc: "down"},
{Key: "/", Desc: "search"},
{Key: "a", Desc: "add"},
{Key: "c", Desc: "comment"},
{Key: "e/d/s/p/t/A/D", Desc: "edit"},
{Key: "x", Desc: "delete"},
{Key: "q", Desc: "quit"},
{Key: "?", Desc: "help"},
},
FullItems: []HelpItem{
{Key: "v", Desc: "kanban"},
{Key: "↑/k", Desc: "up"},
{Key: "↓/j", Desc: "down"},
{Key: "/", Desc: "search"},
{Key: "a", Desc: "add issue"},
{Key: "c", Desc: "add comment"},
{Key: "x", Desc: "delete issue"},
{Key: "e", Desc: "edit title"},
{Key: "d", Desc: "edit description"},
{Key: "s", Desc: "change status"},
{Key: "p", Desc: "change priority"},
{Key: "t", Desc: "change type"},
{Key: "A", Desc: "change assignee"},
{Key: "D", Desc: "edit dependencies"},
{Key: "q", Desc: "quit"},
{Key: "?", Desc: "help"},
},
}
case ViewKanban:
return HelpBarConfig{
ShortItems: []HelpItem{
{Key: "v", Desc: "list view"},
{Key: "↑/k", Desc: "up"},
{Key: "↓/j", Desc: "down"},
{Key: "/", Desc: "search"},
{Key: "n", Desc: "new sprint"},
{Key: "S", Desc: "select sprint"},
{Key: "pgup/pgdn", Desc: "page"},
{Key: "←/→", Desc: "column"},
{Key: "h/l", Desc: "move"},
{Key: "a", Desc: "add"},
{Key: "e/d/s/p/t/A/D", Desc: "edit"},
{Key: "x", Desc: "delete"},
{Key: "q", Desc: "quit"},
{Key: "?", Desc: "help"},
},
FullItems: []HelpItem{
{Key: "v", Desc: "list view"},
{Key: "←/→", Desc: "switch column"},
{Key: "h/l", Desc: "move issue"},
{Key: "↑/k", Desc: "up"},
{Key: "↓/j", Desc: "down"},
{Key: "/", Desc: "search"},
{Key: "pgup", Desc: "page up"},
{Key: "pgdn", Desc: "page down"},
{Key: "a", Desc: "add issue"},
{Key: "x", Desc: "delete issue"},
{Key: "e", Desc: "edit title"},
{Key: "d", Desc: "edit description"},
{Key: "s", Desc: "change status"},
{Key: "p", Desc: "change priority"},
{Key: "t", Desc: "change type"},
{Key: "A", Desc: "change assignee"},
{Key: "D", Desc: "manage dependency"}, // dependencies -> dependency cuz dependencies added a newline
{Key: "S", Desc: "select sprint"},
{Key: "n", Desc: "new sprint"},
{Key: "q", Desc: "quit"},
{Key: "?", Desc: "help"},
},
}
default:
return HelpBarConfig{}
}
}