tui/list/list.go

325 lines
6.7 KiB
Go
Raw Normal View History

2024-07-04 21:22:54 +02:00
package list
import (
"bufio"
"fmt"
"os"
"regexp"
"sort"
"strconv"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/term"
)
var regex = regexp.MustCompile("\033\\[[0-9;]+m")
func removeANSIEscapeCodes(input string) string {
return regex.ReplaceAllString(input, "")
}
type ListData struct {
pages []ListPage
history []int
currentPage int
strLengths []int
localizer *i18n.Localizer
}
type ListPage struct {
Title string
Items []ListItem
Render func() []ListItem
Cache []ListItem
}
type ListItem struct {
Label string
Notice string
Order int
Render func() ListItem
LinkTo int
Value interface{}
}
func New(localizer *i18n.Localizer) ListData {
return ListData{
pages: []ListPage{},
history: []int{},
localizer: localizer,
}
}
func (l *ListData) Execute() interface{} {
return l.listRunner()
}
func (l *ListData) AddPage(page ListPage) {
l.pages = append(l.pages, page)
}
func (l *ListData) listRunner() interface{} {
var option interface{}
for {
var tempItemStore []ListItem
for _, item := range l.pages[l.currentPage].Items {
if len(l.pages[l.currentPage].Cache) == 0 {
if item.Render != nil {
renderedItem := item.Render()
renderedItem.Value = item.Value
tempItemStore = append(tempItemStore, renderedItem)
} else {
tempItemStore = append(tempItemStore, item)
}
sort.SliceStable(tempItemStore, func(i, j int) bool {
return tempItemStore[i].Order < tempItemStore[j].Order
})
}
}
if len(tempItemStore) != 0 {
l.pages[l.currentPage].Cache = append(l.pages[l.currentPage].Cache, tempItemStore...)
if l.currentPage != 0 {
l.pages[l.currentPage].Cache = append(
l.pages[l.currentPage].Cache,
[]ListItem{
{
Label: "Back",
Value: "action_back",
},
}...,
)
}
}
l.renderList()
chosen := l.inputHandler(l.pages[l.currentPage].Cache)
if chosen.LinkTo != 0 {
width, _, _ := term.GetSize(0)
var totalLineNum int
if width == 0 {
totalLineNum = len(l.strLengths)
} else {
for _, strLength := range l.strLengths {
totalLineNum += ((strLength) / width)
}
}
totalLineNum++ //User input line
for i := 0; i < totalLineNum; i++ {
fmt.Printf("\033[A\033[K\033[0G")
}
l.history = append(l.history, l.currentPage)
l.currentPage = chosen.LinkTo
continue
}
if chosen.Value != nil {
if _, ok := chosen.Value.(string); ok {
if chosen.Value == "action_home" {
l.currentPage = 0
l.history = []int{}
width, _, _ := term.GetSize(0)
var totalLineNum int
if width == 0 {
totalLineNum = len(l.strLengths)
} else {
for _, strLength := range l.strLengths {
totalLineNum += ((strLength) / width)
}
}
totalLineNum++ //User input line
for i := 0; i < totalLineNum; i++ {
fmt.Printf("\033[A\033[K\033[0G")
}
continue
}
if chosen.Value == "action_back" {
l.currentPage = l.history[len(l.history)-1]
if len(l.history) == 1 {
l.history = []int{}
} else {
l.history = l.history[0 : len(l.history)-2]
}
width, _, _ := term.GetSize(0)
var totalLineNum int
if width == 0 {
totalLineNum = len(l.strLengths)
} else {
for _, strLength := range l.strLengths {
totalLineNum += ((strLength) / width)
}
}
totalLineNum++ //User input line
for i := 0; i < totalLineNum; i++ {
fmt.Printf("\033[A\033[K\033[0G")
}
continue
}
}
option = chosen.Value
break
}
}
return option
}
func (l *ListData) renderList() {
l.strLengths = []int{}
currentPage := l.pages[l.currentPage]
listNotice := fmt.Sprintf("\033[1m\033[38;5;247m[\033[38;5;214m!\033[38;5;247m]\033[22m \033[3mPlease choose an option from 1 - %d\033[0m\n", len(currentPage.Cache))
l.strLengths = append(l.strLengths, len(removeANSIEscapeCodes(listNotice)))
fmt.Print(listNotice)
listTitle := fmt.Sprintf("\033[1m\033[38;5;247m\033[4m%s:\033[0m\n", currentPage.Title)
l.strLengths = append(l.strLengths, len(removeANSIEscapeCodes(listTitle)))
fmt.Print(listTitle)
longestStrLength := 0
for _, item := range currentPage.Cache {
formattedLabel := removeANSIEscapeCodes(item.Label)
if len([]rune(formattedLabel)) > longestStrLength {
longestStrLength = len([]rune(formattedLabel))
}
}
for index, item := range currentPage.Cache {
var listItem string
var userInputColor string
if index == len(currentPage.Cache)-1 {
userInputColor = "\033[3m\033[38;5;214m"
}
if _, ok := item.Value.(string); ok {
if item.Value == "action_back" {
listItem = fmt.Sprintf(" \033[38;5;247m[\033[1m\033[38;5;167m%d\033[22m\033[38;5;247m]\033[0m %-*s \033[3m\033[38;5;247m%s\033[0m%s\n", index+1, longestStrLength, item.Label, item.Notice, userInputColor)
} else {
listItem = fmt.Sprintf(" \033[38;5;247m[\033[1m\033[38;5;214m%d\033[22m\033[38;5;247m]\033[0m %-*s \033[3m\033[38;5;247m%s\033[0m%s\n", index+1, longestStrLength, item.Label, item.Notice, userInputColor)
}
} else {
listItem = fmt.Sprintf(" \033[38;5;247m[\033[1m\033[38;5;214m%d\033[22m\033[38;5;247m]\033[0m %-*s \033[3m\033[38;5;247m%s\033[0m%s\n", index+1, longestStrLength, item.Label, item.Notice, userInputColor)
}
l.strLengths = append(l.strLengths, len(removeANSIEscapeCodes(listItem)))
fmt.Print(listItem)
}
}
func (l *ListData) inputHandler(items []ListItem) ListItem {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanLines)
var input int
for scanner.Scan() {
text := scanner.Text()
inputAnswer, err := strconv.Atoi(text)
if err != nil {
width, _, _ := term.GetSize(0)
var totalLineNum int
if width == 0 {
totalLineNum = len(l.strLengths)
} else {
for _, strLength := range l.strLengths {
totalLineNum += ((strLength) / width)
}
}
totalLineNum++ //User input line
for i := 0; i < totalLineNum; i++ {
fmt.Printf("\033[0m\033[A\033[K\033[0G")
}
fmt.Printf("\033[1m\033[38;5;247m[\033[38;5;167m!\033[38;5;247m]\033[0m Invalid input, please try again!\n")
l.strLengths = []int{}
l.renderList()
continue
}
if inputAnswer < 1 || inputAnswer > len(items) {
width, _, _ := term.GetSize(0)
var totalLineNum int
if width == 0 {
totalLineNum = len(l.strLengths)
} else {
for _, strLength := range l.strLengths {
totalLineNum += ((strLength) / width)
}
}
totalLineNum++ //User input line
for i := 0; i < totalLineNum; i++ {
fmt.Printf("\033[A\033[K\033[0G")
}
fmt.Printf("\033[1m\033[38;5;247m[\033[38;5;167m!\033[38;5;247m]\033[0m Invalid input, please try again!\n")
l.strLengths = []int{}
l.renderList()
continue
}
input = inputAnswer
break
}
chosenItem := items[input-1]
return chosenItem
}