2024-11-01 21:04:01 +01:00
|
|
|
package list
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2024-11-11 22:36:22 +01:00
|
|
|
"egtyl.xyz/omnibill/tui"
|
2024-11-01 21:04:01 +01:00
|
|
|
"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 := tui.Format(tui.FmtBold, tui.FgColorGrey) +
|
|
|
|
"[" + tui.FgColorGold + "!" + tui.FgColorGrey + "]" + tui.FmtBoldReset + " " +
|
|
|
|
tui.FmtItalic + fmt.Sprintf("Please choose an option from 1 - %d", len(currentPage.Cache)) + "\n"
|
|
|
|
|
|
|
|
//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 := tui.FgColorGrey + "[ " + tui.Format(tui.FgColorGold, tui.FmtUnderline) + currentPage.Title + ":" + tui.Format(tui.FmtUnderlineReset, tui.FgColorGrey) + " ]" + tui.FmtReset + "\n"
|
|
|
|
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 = tui.Format(tui.FmtItalic, tui.FgColorGrey)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := item.Value.(string); ok {
|
|
|
|
if item.Value == "action_back" {
|
|
|
|
listItem = " " + tui.FgColorGrey + "[" + tui.Format(tui.FmtBold, tui.FgColorRed) + strconv.Itoa(index+1) + tui.Format(tui.FmtBoldReset, tui.FgColorGrey) + "]" + tui.FmtReset +
|
|
|
|
fmt.Sprintf(" %-*s ", longestStrLength, item.Label) + tui.Format(tui.FmtItalic, tui.FgColorGrey) + item.Notice + tui.FmtReset + userInputColor + "\n"
|
|
|
|
//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 = " " + tui.FgColorGrey + "[" + tui.Format(tui.FmtBold, tui.FgColorGold) + strconv.Itoa(index+1) + tui.Format(tui.FmtBoldReset, tui.FgColorGrey) + "]" + tui.FmtReset +
|
|
|
|
fmt.Sprintf(" %-*s ", longestStrLength, item.Label) + tui.Format(tui.FmtItalic, tui.FgColorGrey) + item.Notice + tui.FmtReset + userInputColor + "\n"
|
|
|
|
//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 = " " + tui.FgColorGrey + "[" + tui.Format(tui.FmtBold, tui.FgColorGold) + strconv.Itoa(index+1) + tui.Format(tui.FmtBoldReset, tui.FgColorGrey) + "]" + tui.FmtReset +
|
|
|
|
fmt.Sprintf(" %-*s ", longestStrLength, item.Label) + tui.Format(tui.FmtItalic, tui.FgColorGrey) + item.Notice + tui.FmtReset + userInputColor + "\n"
|
|
|
|
//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(
|
|
|
|
tui.Format(tui.FmtBold, tui.FgColorGrey) +
|
|
|
|
"[" + tui.FgColorRed + "!" + tui.FgColorGrey + "]" + tui.FmtBoldReset + " " +
|
|
|
|
" Invalid input, please try again!\n",
|
|
|
|
)
|
|
|
|
//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(
|
|
|
|
tui.Format(tui.FmtBold, tui.FgColorGrey) +
|
|
|
|
"[" + tui.FgColorRed + "!" + tui.FgColorGrey + "]" + tui.FmtBoldReset + " " +
|
|
|
|
" Invalid input, please try again!\n",
|
|
|
|
)
|
|
|
|
//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
|
|
|
|
}
|