Skip to content

Commit

Permalink
refactor: use a selector type to select items
Browse files Browse the repository at this point in the history
Use a proper type to keep track of selected items. The paginator bubble
shouldn't be abused like this if it's not intended to be part of the
program UI.
  • Loading branch information
aymanbagabas committed Jul 25, 2024
1 parent 06892e6 commit 0f670f9
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 64 deletions.
51 changes: 25 additions & 26 deletions form.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/paginator"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh/internal/selector"

Check failure on line 14 in form.go

View workflow job for this annotation

GitHub Actions / test (stable, ubuntu-latest)

no required module provides package github.com/charmbracelet/huh/internal/selector; to add it:

Check failure on line 14 in form.go

View workflow job for this annotation

GitHub Actions / test (1.22, ubuntu-latest)

no required module provides package github.com/charmbracelet/huh/internal/selector; to add it:

Check failure on line 14 in form.go

View workflow job for this annotation

GitHub Actions / test (1.22, ubuntu-latest)

no required module provides package github.com/charmbracelet/huh/internal/selector; to add it:

Check failure on line 14 in form.go

View workflow job for this annotation

GitHub Actions / test (stable, ubuntu-latest)

no required module provides package github.com/charmbracelet/huh/internal/selector; to add it:
)

const defaultWidth = 80
Expand Down Expand Up @@ -65,7 +65,7 @@ type Form struct {
results map[string]any

// navigation
paginator paginator.Model
selector *selector.Selector

// callbacks
SubmitCmd tea.Cmd
Expand Down Expand Up @@ -97,15 +97,14 @@ type Form struct {
// Use With* methods to customize the form with options, such as setting
// different themes and keybindings.
func NewForm(groups ...*Group) *Form {
p := paginator.New()
p.SetTotalPages(len(groups))
selector := selector.NewSelector(len(groups))

f := &Form{
groups: groups,
paginator: p,
keymap: NewDefaultKeyMap(),
results: make(map[string]any),
layout: LayoutDefault,
groups: groups,
selector: selector,
keymap: NewDefaultKeyMap(),
results: make(map[string]any),
layout: LayoutDefault,
teaOptions: []tea.ProgramOption{
tea.WithOutput(os.Stderr),
},
Expand Down Expand Up @@ -400,18 +399,18 @@ func (f *Form) UpdateFieldPositions() *Form {

// Errors returns the current groups' errors.
func (f *Form) Errors() []error {
return f.groups[f.paginator.Page].Errors()
return f.groups[f.selector.Selected()].Errors()
}

// Help returns the current groups' help.
func (f *Form) Help() help.Model {
return f.groups[f.paginator.Page].help
return f.groups[f.selector.Selected()].help
}

// KeyBinds returns the current fields' keybinds.
func (f *Form) KeyBinds() []key.Binding {
group := f.groups[f.paginator.Page]
return group.fields[group.paginator.Page].KeyBinds()
group := f.groups[f.selector.Selected()]
return group.fields[f.selector.Selected()].KeyBinds()
}

// Get returns a result from the form.
Expand Down Expand Up @@ -480,7 +479,7 @@ func (f *Form) Init() tea.Cmd {
cmds[i] = group.Init()
}

if f.isGroupHidden(f.paginator.Page) {
if f.isGroupHidden(f.selector.Selected()) {
cmds = append(cmds, nextGroup)
}

Expand All @@ -494,7 +493,7 @@ func (f *Form) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return f, nil
}

page := f.paginator.Page
page := f.selector.Selected()
group := f.groups[page]

switch msg := msg.(type) {
Expand Down Expand Up @@ -525,7 +524,7 @@ func (f *Form) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

case nextFieldMsg:
// Form is progressing to the next field, let's save the value of the current field.
field := group.fields[group.paginator.Page]
field := group.fields[f.selector.Selected()]
f.results[field.GetKey()] = field.GetValue()

case nextGroupMsg:
Expand All @@ -539,38 +538,38 @@ func (f *Form) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return f, f.SubmitCmd
}

if f.paginator.OnLastPage() {
if f.selector.OnLast() {
return submit()
}

for i := f.paginator.Page + 1; i < f.paginator.TotalPages; i++ {
for i := f.selector.Selected() + 1; i < f.selector.Total(); i++ {
if !f.isGroupHidden(i) {
f.paginator.Page = i
f.selector.SetSelected(i)
break
}
// all subsequent groups are hidden, so we must act as
// if we were in the last one.
if i == f.paginator.TotalPages-1 {
if i == f.selector.Total()-1 {
return submit()
}
}
f.groups[f.paginator.Page].active = true
return f, f.groups[f.paginator.Page].Init()
f.groups[f.selector.Selected()].active = true
return f, f.groups[f.selector.Selected()].Init()

case prevGroupMsg:
if len(group.Errors()) > 0 {
return f, nil
}

for i := f.paginator.Page - 1; i >= 0; i-- {
for i := f.selector.Selected() - 1; i >= 0; i-- {
if !f.isGroupHidden(i) {
f.paginator.Page = i
f.selector.SetSelected(i)
break
}
}

f.groups[f.paginator.Page].active = true
return f, f.groups[f.paginator.Page].Init()
f.groups[f.selector.Selected()].active = true
return f, f.groups[f.selector.Selected()].Init()
}

m, cmd := group.Update(msg)
Expand Down
64 changes: 32 additions & 32 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"strings"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/paginator"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh/internal/selector"
"github.com/charmbracelet/lipgloss"
)

Expand All @@ -18,15 +18,15 @@ import (
// progress to the next group.
type Group struct {
// collection of fields
fields []Field
fields []Field
selector *selector.Selector

// information
title string
description string

// navigation
paginator paginator.Model
viewport viewport.Model
viewport viewport.Model

// help
showHelp bool
Expand All @@ -45,12 +45,10 @@ type Group struct {

// NewGroup returns a new group with the given fields.
func NewGroup(fields ...Field) *Group {
p := paginator.New()
p.SetTotalPages(len(fields))

selector := selector.NewSelector(len(fields))
group := &Group{
fields: fields,
paginator: p,
selector: selector,
help: help.New(),
showHelp: true,
showErrors: true,
Expand Down Expand Up @@ -190,17 +188,17 @@ func PrevField() tea.Msg {
func (g *Group) Init() tea.Cmd {
var cmds []tea.Cmd

if g.fields[g.paginator.Page].Skip() {
if g.paginator.OnLastPage() {
if g.fields[g.selector.Selected()].Skip() {
if g.selector.OnLast() {
cmds = append(cmds, g.prevField()...)
} else if g.paginator.Page == 0 {
} else if g.selector.OnFirst() {
cmds = append(cmds, g.nextField()...)
}
return tea.Batch(cmds...)
}

if g.active {
cmd := g.fields[g.paginator.Page].Focus()
cmd := g.fields[g.selector.Selected()].Focus()
cmds = append(cmds, cmd)
}
g.buildView()
Expand All @@ -209,35 +207,37 @@ func (g *Group) Init() tea.Cmd {

// nextField moves to the next field.
func (g *Group) nextField() []tea.Cmd {
blurCmd := g.fields[g.paginator.Page].Blur()
if g.paginator.OnLastPage() {
blurCmd := g.fields[g.selector.Selected()].Blur()
if g.selector.OnLast() {
return []tea.Cmd{blurCmd, nextGroup}
}
g.paginator.NextPage()
for g.fields[g.paginator.Page].Skip() {
if g.paginator.OnLastPage() {
g.selector.Next()
for g.fields[g.selector.Selected()].Skip() {
if g.selector.OnLast() {
return []tea.Cmd{blurCmd, nextGroup}
}
g.paginator.NextPage()
g.selector.Next()
}
focusCmd := g.fields[g.paginator.Page].Focus()
focusCmd := g.fields[g.selector.Selected()].Focus()
return []tea.Cmd{blurCmd, focusCmd}
}

// prevField moves to the previous field.
func (g *Group) prevField() []tea.Cmd {
blurCmd := g.fields[g.paginator.Page].Blur()
if g.paginator.Page <= 0 {
blurCmd := g.fields[g.selector.Selected()].Blur()
if g.selector.OnFirst() {
return []tea.Cmd{blurCmd, prevGroup}
} else {
g.selector.Prev()
}
g.paginator.PrevPage()
for g.fields[g.paginator.Page].Skip() {
if g.paginator.Page <= 0 {
for g.fields[g.selector.Selected()].Skip() {
if g.selector.OnFirst() {
return []tea.Cmd{blurCmd, prevGroup}
} else {
g.selector.Prev()
}
g.paginator.PrevPage()
}
focusCmd := g.fields[g.paginator.Page].Focus()
focusCmd := g.fields[g.selector.Selected()].Focus()
return []tea.Cmd{blurCmd, focusCmd}
}

Expand All @@ -255,7 +255,7 @@ func (g *Group) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
g.fields[i] = m.(Field)
cmds = append(cmds, cmd)
}
if g.paginator.Page == i {
if g.selector.Selected() == i {
m, cmd := g.fields[i].Update(msg)
g.fields[i] = m.(Field)
cmds = append(cmds, cmd)
Expand Down Expand Up @@ -294,13 +294,13 @@ func (g *Group) getContent() (int, string) {
gap := "\n\n"

// if the focused field is requesting it be zoomed, only show that field.
if g.fields[g.paginator.Page].Zoom() {
g.fields[g.paginator.Page].WithHeight(g.height - 1)
fields.WriteString(g.fields[g.paginator.Page].View())
if g.fields[g.selector.Selected()].Zoom() {
g.fields[g.selector.Selected()].WithHeight(g.height - 1)
fields.WriteString(g.fields[g.selector.Selected()].View())
} else {
for i, field := range g.fields {
fields.WriteString(field.View())
if i == g.paginator.Page {
if i == g.selector.Selected() {
offset = lipgloss.Height(fields.String()) - lipgloss.Height(field.View())
}
if i < len(g.fields)-1 {
Expand Down Expand Up @@ -339,7 +339,7 @@ func (g *Group) Footer() string {
view.WriteRune('\n')
errors := g.Errors()
if g.showHelp && len(errors) <= 0 {
view.WriteString(g.help.ShortHelpView(g.fields[g.paginator.Page].KeyBinds()))
view.WriteString(g.help.ShortHelpView(g.fields[g.selector.Selected()].KeyBinds()))
}
if g.showErrors {
for _, err := range errors {
Expand Down
12 changes: 6 additions & 6 deletions layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func LayoutGrid(rows int, columns int) Layout {
type layoutDefault struct{}

func (l *layoutDefault) View(f *Form) string {
return f.groups[f.paginator.Page].View()
return f.groups[f.selector.Selected()].View()
}

func (l *layoutDefault) GroupWidth(_ *Form, _ *Group, w int) int {
Expand All @@ -43,7 +43,7 @@ type layoutColumns struct {
}

func (l *layoutColumns) visibleGroups(f *Form) []*Group {
segmentIndex := f.paginator.Page / l.columns
segmentIndex := f.selector.Selected() / l.columns
start := segmentIndex * l.columns
end := start + l.columns

Expand All @@ -64,7 +64,7 @@ func (l *layoutColumns) View(f *Form) string {
for _, group := range groups {
columns = append(columns, group.Content())
}
footer := f.groups[f.paginator.Page].Footer()
footer := f.groups[f.selector.Selected()].Footer()

return lipgloss.JoinVertical(lipgloss.Left,
lipgloss.JoinHorizontal(lipgloss.Top, columns...),
Expand All @@ -83,7 +83,7 @@ func (l *layoutStack) View(f *Form) string {
for _, group := range f.groups {
columns = append(columns, group.Content())
}
footer := f.groups[f.paginator.Page].Footer()
footer := f.groups[f.selector.Selected()].Footer()

var view strings.Builder
view.WriteString(strings.Join(columns, "\n"))
Expand All @@ -101,7 +101,7 @@ type layoutGrid struct {

func (l *layoutGrid) visibleGroups(f *Form) [][]*Group {
total := l.rows * l.columns
segmentIndex := f.paginator.Page / total
segmentIndex := f.selector.Selected() / total
start := segmentIndex * total
end := start + total

Expand Down Expand Up @@ -139,7 +139,7 @@ func (l *layoutGrid) View(f *Form) string {
}
rows = append(rows, lipgloss.JoinHorizontal(lipgloss.Top, columns...))
}
footer := f.groups[f.paginator.Page].Footer()
footer := f.groups[f.selector.Selected()].Footer()

return lipgloss.JoinVertical(lipgloss.Left, strings.Join(rows, "\n"), footer)
}
Expand Down

0 comments on commit 0f670f9

Please sign in to comment.