refactor: add dynamic groups with height and pagination

This commit is contained in:
Slug-Boi
2024-11-25 16:30:26 +01:00
parent f662eb15b3
commit abd21e41a5
2 changed files with 129 additions and 143 deletions
+65 -23
View File
@@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/Slug-Boi/cocommit/src/cmd/utils" "github.com/Slug-Boi/cocommit/src/cmd/utils"
"github.com/charmbracelet/bubbles/paginator"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/term" "github.com/charmbracelet/x/term"
@@ -30,8 +31,11 @@ var (
type mainModel struct { type mainModel struct {
content []string content []string
index int index int
paginator paginator.Model
} }
var cap, lines int
func newModel() mainModel { func newModel() mainModel {
groups := utils.Groups groups := utils.Groups
@@ -48,7 +52,42 @@ func newModel() mainModel {
slices.Sort(content) slices.Sort(content)
m := mainModel{content: content} // check if terminal 0 is a terminal
var w,h int
var err error
if ok := term.IsTerminal(0); ok {
// calculate term size
w, h, err = term.GetSize(0)
if err != nil {
panic(err)
}
}
if h > 20 {
lines = 2
} else {
lines = 1
}
// if the terminal size is 0, then skip
if w > 0 {
// 30 is a magic number don't question it
cap = min(5, w/30)
}
cap = max(1, cap)
if cap * 2 > len(content) {
cap = len(content)
}
p := paginator.New()
p.Type = paginator.Dots
p.ActiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}).Render("•")
p.InactiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "250", Dark: "238"}).Render("•")
p.PerPage = cap * lines
p.SetTotalPages(len(content))
m := mainModel{content: content, paginator: p}
return m return m
} }
@@ -58,7 +97,7 @@ func (m mainModel) Init() tea.Cmd {
} }
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
//var cmd tea.Cmd var cmd tea.Cmd
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
@@ -90,15 +129,16 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
return nil, nil return nil, nil
case "tab", "right": case "tab", "right":
m.Next() m.next()
case "left": case "left":
if m.index == 0 { m.previous()
m.index = len(m.content) - 1
} else {
m.index--
}
} }
} }
// Adrian is a fucking genius thanks for the idea :)
m.paginator.Page = m.index / (cap * lines)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...) return m, tea.Batch(cmds...)
} }
@@ -116,23 +156,17 @@ func (m mainModel) View() string {
// Take the first 5 elements and join them horizontally // Take the first 5 elements and join them horizontally
// then take the next 5 and join them horizontally if there are more than 5 // then take the next 5 and join them horizontally if there are more than 5
// then join vertically // then join vertically
//TODO: Figure out what width is measured in and tie the number 5 to a variable that
// is width_of_term/item_width start, end := m.paginator.GetSliceBounds(len(m.content))
w, _, err := term.GetSize(0) end = end - 1
if err != nil { //println(start, end)
panic(err) for start <= end {
} s += lipgloss.JoinHorizontal(lipgloss.Top, squares[start:start+cap]...)
// 30 is a magic number don't question it
cap := w / 30
for len(squares) > cap {
s += lipgloss.JoinHorizontal(lipgloss.Top, squares[:cap]...)
s += "\n" s += "\n"
squares = squares[cap:] start += cap
} }
s += lipgloss.JoinHorizontal(lipgloss.Top, squares...) s += "\n" + m.paginator.View()
//s += lipgloss.JoinHorizontal(lipgloss.Top, squares...)
s += helpStyle.Render("\ntab/right: focus next • left: focus previous • enter: select group • q/esq: exit\n") s += helpStyle.Render("\ntab/right: focus next • left: focus previous • enter: select group • q/esq: exit\n")
return s return s
@@ -145,10 +179,18 @@ func (m mainModel) currentFocusedModel() string {
return "" return ""
} }
func (m *mainModel) Next() { func (m *mainModel) next() {
if m.index == len(m.content)-1 { if m.index == len(m.content)-1 {
m.index = 0 m.index = 0
} else { } else {
m.index++ m.index++
} }
} }
func (m *mainModel) previous() {
if m.index == 0 {
m.index = len(m.content) - 1
} else {
m.index--
}
}
+62 -118
View File
@@ -13,7 +13,7 @@ import (
) )
const author_data = `syntax for the test file const author_data = `syntax for the test file
te|testing|TestUser|test@test.test|ex te|testing|TestUser|test@test.test|ex;;gr0
ti|testtest|UserName2|testing@user.io;;gr1` ti|testtest|UserName2|testing@user.io;;gr1`
var envVar string var envVar string
@@ -36,6 +36,13 @@ func teardown() {
os.Setenv("author_file", envVar) os.Setenv("author_file", envVar)
} }
func keyPress(tm *teatest.TestModel, key string) {
tm.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune(key),
})
}
// tui_show_users TESTS BEGIN // tui_show_users TESTS BEGIN
func TestShowUser(t *testing.T) { func TestShowUser(t *testing.T) {
setup() setup()
@@ -50,10 +57,7 @@ func TestShowUser(t *testing.T) {
return bytes.Contains(bts, []byte("syntax for the test file")) return bytes.Contains(bts, []byte("syntax for the test file"))
}, teatest.WithCheckInterval(time.Millisecond*100), teatest.WithDuration(time.Second*2)) }, teatest.WithCheckInterval(time.Millisecond*100), teatest.WithDuration(time.Second*2))
tm.Send(tea.KeyMsg{ keyPress(tm, "q")
Type: tea.KeyRunes,
Runes: []rune("q"),
})
tm.WaitFinished(t, teatest.WithFinalTimeout(time.Second)) tm.WaitFinished(t, teatest.WithFinalTimeout(time.Second))
@@ -72,34 +76,19 @@ func TestEntryTA(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "T")
Type: tea.KeyRunes,
Runes: []rune("T"),
})
tm.Type("test") tm.Type("test")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Type("testtest@temp.io") tm.Type("testtest@temp.io")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "esc")
Type: tea.KeyRunes,
Runes: []rune("esc"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -133,57 +122,27 @@ func Test_EntryCA(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "C")
Type: tea.KeyRunes,
Runes: []rune("C"),
})
tm.Type("test") tm.Type("test")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Type("testing2") tm.Type("testing2")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Type("TestUser") tm.Type("TestUser")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Type("test@temp.io") tm.Type("test@temp.io")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Type("gr6") tm.Type("gr6")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes, keyPress(tm, "enter")
Runes: []rune("enter"), keyPress(tm, "tab")
}) keyPress(tm, "enter")
tm.Send(tea.KeyMsg{ keyPress(tm, "esc")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("tab"),
})
tm.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Send(tea.KeyMsg{
Type: tea.KeyRunes,
Runes: []rune("esc"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -230,10 +189,7 @@ func Test_EntryCM(t *testing.T) {
) )
tm.Type("test commit message") tm.Type("test commit message")
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model_cm) m, ok := fm.(model_cm)
@@ -259,15 +215,9 @@ func Test_EntrySelectUsers(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, " ")
Type: tea.KeyRunes,
Runes: []rune(" "),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -295,15 +245,9 @@ func Test_EntrySelectAll(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "A")
Type: tea.KeyRunes,
Runes: []rune("A"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -330,15 +274,9 @@ func Test_EntryNegation(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "n")
Type: tea.KeyRunes,
Runes: []rune("n"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -365,20 +303,11 @@ func Test_EntryDeleteAuthor(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "D")
Type: tea.KeyRunes,
Runes: []rune("D"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "D")
Type: tea.KeyRunes,
Runes: []rune("D"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -407,20 +336,11 @@ func Test_GroupSelection(t *testing.T) {
tm := teatest.NewTestModel( tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(300, 300), t, m, teatest.WithInitialTermSize(300, 300),
) )
tm.Send(tea.KeyMsg{ keyPress(tm, "f")
Type: tea.KeyRunes,
Runes: []rune("f"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
tm.Send(tea.KeyMsg{ keyPress(tm, "enter")
Type: tea.KeyRunes,
Runes: []rune("enter"),
})
fm := tm.FinalModel(t) fm := tm.FinalModel(t)
m, ok := fm.(model) m, ok := fm.(model)
@@ -429,7 +349,31 @@ func Test_GroupSelection(t *testing.T) {
} }
if len(selected) != 1 { if len(selected) != 1 {
t.Errorf("Expected 1 selected item, got %d", len(selected)) t.Errorf("Expected not 1 selected item, got %d", len(selected))
}
}
func Test_pagination(t *testing.T) {
setup()
defer teardown()
m := mainModel{}
tm := teatest.NewTestModel(
t, m, teatest.WithInitialTermSize(25, 25),
)
keyPress(tm, "right")
tm.Quit()
fm := tm.FinalModel(t)
m, ok := fm.(mainModel)
if !ok {
t.Errorf("Expected model, got %T", fm)
}
if m.paginator.Page != 1 {
t.Errorf("Expected page 1, got %d", m.paginator.Page)
} }
} }