diff --git a/cmd/cli/tui/footer/footer.go b/cmd/cli/tui/footer/footer.go index 98869a9..d7e021c 100644 --- a/cmd/cli/tui/footer/footer.go +++ b/cmd/cli/tui/footer/footer.go @@ -10,8 +10,7 @@ import ( "github.com/jonathanhope/armaria/cmd/cli/tui/utils" ) -const HelpInfoWidth = 7 // width of the help info in the footer -const TextInputName = "BooksInput" // name of the textinput +const HelpInfoWidth = 7 // width of the help info in the footer // FooterModel is the model for a header. // The footer can collect input, and displays informationa about the apps state. @@ -20,6 +19,7 @@ type FooterModel struct { width int // max width of the footer inputMode bool // if true footer is accepting input filters []string // currently applied filters + inputName string // the name of the input in the footer input textinput.TextInputModel // allows text input } @@ -35,9 +35,12 @@ func (m FooterModel) InputMode() bool { // InitialModel builds the model. func InitialModel(name string) FooterModel { + inputName := name + "Input" + return FooterModel{ - name: name, - input: textinput.InitialModel(TextInputName, ""), + name: name, + inputName: inputName, + input: textinput.InitialModel(inputName, ""), } } @@ -53,7 +56,7 @@ func (m FooterModel) Update(msg tea.Msg) (FooterModel, tea.Cmd) { } else { var inputCmd tea.Cmd m.input, inputCmd = m.input.Update(msgs.SizeMsg{ - Name: TextInputName, + Name: m.inputName, Width: m.width - HelpInfoWidth, }) cmds = append(cmds, inputCmd) @@ -65,19 +68,19 @@ func (m FooterModel) Update(msg tea.Msg) (FooterModel, tea.Cmd) { if m.inputMode { cmds = append(cmds, func() tea.Msg { - return msgs.PromptMsg{Name: TextInputName, Prompt: msg.Prompt} + return msgs.PromptMsg{Name: m.inputName, Prompt: msg.Prompt} }, func() tea.Msg { - return msgs.TextMsg{Name: TextInputName, Text: msg.Text} + return msgs.TextMsg{Name: m.inputName, Text: msg.Text} }, func() tea.Msg { - return msgs.FocusMsg{Name: TextInputName, MaxChars: msg.MaxChars} + return msgs.FocusMsg{Name: m.inputName, MaxChars: msg.MaxChars} }) } else { cmds = append(cmds, func() tea.Msg { - return msgs.BlurMsg{Name: TextInputName} + return msgs.BlurMsg{Name: m.inputName} }, func() tea.Msg { - return msgs.PromptMsg{Name: TextInputName, Prompt: ""} + return msgs.PromptMsg{Name: m.inputName, Prompt: ""} }, func() tea.Msg { - return msgs.TextMsg{Name: TextInputName, Text: ""} + return msgs.TextMsg{Name: m.inputName, Text: ""} }) } } diff --git a/cmd/cli/tui/footer/footer_test.go b/cmd/cli/tui/footer/footer_test.go index 06aef20..0de1795 100644 --- a/cmd/cli/tui/footer/footer_test.go +++ b/cmd/cli/tui/footer/footer_test.go @@ -14,13 +14,15 @@ const Name = "footer" func TestCanUpdateWidth(t *testing.T) { gotModel := FooterModel{ - name: Name, + name: Name, + inputName: Name + "Input", } gotModel, gotCmd := gotModel.Update(msgs.SizeMsg{Name: Name, Width: 1}) wantModel := FooterModel{ - name: Name, - width: 1, + name: Name, + inputName: Name + "Input", + width: 1, } verifyUpdate(t, gotModel, wantModel, gotCmd, nil) @@ -28,7 +30,8 @@ func TestCanUpdateWidth(t *testing.T) { func TestCanStartInputMode(t *testing.T) { gotModel := FooterModel{ - name: Name, + name: Name, + inputName: Name + "Input", } gotModel, gotCmd := gotModel.Update(msgs.InputModeMsg{ Name: Name, @@ -40,14 +43,15 @@ func TestCanStartInputMode(t *testing.T) { wantModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } wantCmd := func() tea.Msg { return tea.BatchMsg{ - func() tea.Msg { return msgs.PromptMsg{Name: TextInputName, Prompt: "prompt"} }, - func() tea.Msg { return msgs.TextMsg{Name: TextInputName, Text: "text"} }, - func() tea.Msg { return msgs.FocusMsg{Name: TextInputName, MaxChars: 5} }, + func() tea.Msg { return msgs.PromptMsg{Name: Name + "Input", Prompt: "prompt"} }, + func() tea.Msg { return msgs.TextMsg{Name: Name + "Input", Text: "text"} }, + func() tea.Msg { return msgs.FocusMsg{Name: Name + "Input", MaxChars: 5} }, } } @@ -57,20 +61,22 @@ func TestCanStartInputMode(t *testing.T) { func TestCanEndInputMode(t *testing.T) { gotModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } gotModel, gotCmd := gotModel.Update(msgs.InputModeMsg{Name: Name, InputMode: false}) wantModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: false, } wantCmd := func() tea.Msg { return tea.BatchMsg{ - func() tea.Msg { return msgs.BlurMsg{Name: TextInputName} }, - func() tea.Msg { return msgs.PromptMsg{Name: TextInputName} }, - func() tea.Msg { return msgs.TextMsg{Name: TextInputName} }, + func() tea.Msg { return msgs.BlurMsg{Name: Name + "Input"} }, + func() tea.Msg { return msgs.PromptMsg{Name: Name + "Input"} }, + func() tea.Msg { return msgs.TextMsg{Name: Name + "Input"} }, } } @@ -79,13 +85,15 @@ func TestCanEndInputMode(t *testing.T) { func TestCanSetFilters(t *testing.T) { gotModel := FooterModel{ - name: Name, + name: Name, + inputName: Name + "Input", } gotModel, gotCmd := gotModel.Update(msgs.FiltersMsg{Name: Name, Filters: []string{"one"}}) wantModel := FooterModel{ - name: Name, - filters: []string{"one"}, + name: Name, + inputName: Name + "Input", + filters: []string{"one"}, } verifyUpdate(t, gotModel, wantModel, gotCmd, nil) @@ -94,12 +102,14 @@ func TestCanSetFilters(t *testing.T) { func TestCanCancelInput(t *testing.T) { gotModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } gotModel, gotCmd := gotModel.Update(tea.KeyMsg{Type: tea.KeyEsc}) wantModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } @@ -115,12 +125,14 @@ func TestCanCancelInput(t *testing.T) { func TestCanConfirmInput(t *testing.T) { gotModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } gotModel, gotCmd := gotModel.Update(tea.KeyMsg{Type: tea.KeyEnter}) wantModel := FooterModel{ name: Name, + inputName: Name + "Input", inputMode: true, } diff --git a/cmd/cli/tui/scrolltable/scrolltable.go b/cmd/cli/tui/scrolltable/scrolltable.go index 0ee2eb8..6dc57b7 100644 --- a/cmd/cli/tui/scrolltable/scrolltable.go +++ b/cmd/cli/tui/scrolltable/scrolltable.go @@ -178,6 +178,11 @@ func (m ScrolltableModel[T]) Update(msg tea.Msg) (ScrolltableModel[T], tea.Cmd) case msgs.DataMsg[T]: if msg.Name == m.name { + // This allows the cursor to stick in the right place when an item is removed. + if len(m.data)-1 == len(msg.Data) && m.frameStart > 0 { + m.frameStart -= 1 + } + m.data = msg.Data m.resetFrame(msg.Move) return m, tea.Batch(m.selectionChangedCmd(), func() tea.Msg { return msgs.FreeMsg{} })