Added status and prompt
This commit is contained in:
parent
b0909ffe4e
commit
81cd1d0971
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/aws/aws-sdk-go-v2/config"
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||||
"github.com/brianvoe/gofakeit/v6"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
||||||
"github.com/lmika/awstools/internal/common/ui/dispatcher"
|
"github.com/lmika/awstools/internal/common/ui/dispatcher"
|
||||||
|
@ -19,6 +18,7 @@ import (
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
||||||
"github.com/lmika/gopkgs/cli"
|
"github.com/lmika/gopkgs/cli"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -65,9 +65,12 @@ func main() {
|
||||||
_ = uiModel
|
_ = uiModel
|
||||||
// END TEMP
|
// END TEMP
|
||||||
|
|
||||||
model := layout.FullScreen(layout.NewVBox(
|
model := layout.FullScreen(statusandprompt.New(
|
||||||
frame.NewFrame("This is the header", layout.Model(newTestModel("this is the top"))),
|
layout.NewVBox(
|
||||||
frame.NewFrame("This is another header", layout.Model(newTestModel("this is the bottom"))),
|
frame.NewFrame("This is the header", true, layout.Model(newTestModel("this is the top"))),
|
||||||
|
frame.NewFrame("This is another header", false, layout.Model(newTestModel("this is the bottom"))),
|
||||||
|
),
|
||||||
|
"Hello world",
|
||||||
))
|
))
|
||||||
|
|
||||||
//frameSet := frameset.New([]frameset.Frame{
|
//frameSet := frameset.New([]frameset.Frame{
|
||||||
|
@ -126,7 +129,9 @@ func newTestModel(descr string) tea.Model {
|
||||||
OnKeyPressed: func(k string) tea.Cmd {
|
OnKeyPressed: func(k string) tea.Cmd {
|
||||||
log.Println("got key press: " + k)
|
log.Println("got key press: " + k)
|
||||||
if k == "enter" {
|
if k == "enter" {
|
||||||
return modal.PushMode(newTestModel("this is mode " + gofakeit.CarModel() + " (press k to end)"))
|
return statusandprompt.Prompt("What is your car? ", func(val string) tea.Cmd {
|
||||||
|
return statusandprompt.SetStatus("Your car is = " + val)
|
||||||
|
})
|
||||||
} else if k == "k" {
|
} else if k == "k" {
|
||||||
return modal.PopMode
|
return modal.PopMode
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,22 @@ var (
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Foreground(lipgloss.Color("#ffffff")).
|
Foreground(lipgloss.Color("#ffffff")).
|
||||||
Background(lipgloss.Color("#4479ff"))
|
Background(lipgloss.Color("#4479ff"))
|
||||||
|
|
||||||
|
inactiveHeaderStyle = lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#d1d1d1"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Frame is a frame that appears in the
|
// Frame is a frame that appears in the
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
header string
|
header string
|
||||||
|
active bool
|
||||||
model layout.ResizingModel
|
model layout.ResizingModel
|
||||||
width int
|
width int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFrame(header string, model layout.ResizingModel) Frame {
|
func NewFrame(header string, active bool, model layout.ResizingModel) Frame {
|
||||||
return Frame{header, model, 0}
|
return Frame{header, active, model, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Frame) Init() tea.Cmd {
|
func (f Frame) Init() tea.Cmd {
|
||||||
|
@ -31,6 +36,14 @@ func (f Frame) Init() tea.Cmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Frame) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (f Frame) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
// If frame is not active, do not receive key messages
|
||||||
|
if !f.active {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newModel, cmd := f.model.Update(msg)
|
newModel, cmd := f.model.Update(msg)
|
||||||
f.model = newModel.(layout.ResizingModel)
|
f.model = newModel.(layout.ResizingModel)
|
||||||
return f, cmd
|
return f, cmd
|
||||||
|
@ -48,8 +61,13 @@ func (f Frame) Resize(w, h int) layout.ResizingModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Frame) headerView() string {
|
func (f Frame) headerView() string {
|
||||||
|
style := inactiveHeaderStyle
|
||||||
|
if f.active {
|
||||||
|
style = activeHeaderStyle
|
||||||
|
}
|
||||||
|
|
||||||
titleText := f.header
|
titleText := f.header
|
||||||
title := activeHeaderStyle.Render(titleText)
|
title := style.Render(titleText)
|
||||||
line := activeHeaderStyle.Render(strings.Repeat(" ", utils.Max(0, f.width-lipgloss.Width(title))))
|
line := style.Render(strings.Repeat(" ", utils.Max(0, f.width-lipgloss.Width(title))))
|
||||||
return lipgloss.JoinHorizontal(lipgloss.Left, title, line)
|
return lipgloss.JoinHorizontal(lipgloss.Left, title, line)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package statusandprompt
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
type setStatusMsg string
|
||||||
|
|
||||||
|
type startPromptMsg struct {
|
||||||
|
prompt string
|
||||||
|
onDone func(val string) tea.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetStatus(newStatus string) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
return setStatusMsg(newStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prompt(prompt string, onDone func(val string) tea.Cmd) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
return startPromptMsg{
|
||||||
|
prompt: prompt,
|
||||||
|
onDone: onDone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
internal/dynamo-browse/ui/teamodels/statusandprompt/model.go
Normal file
94
internal/dynamo-browse/ui/teamodels/statusandprompt/model.go
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package statusandprompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatusAndPrompt is a resizing model which displays a submodel and a status bar. When the start prompt
|
||||||
|
// event is received, focus will be torn away and the user will be given a prompt the enter text.
|
||||||
|
type StatusAndPrompt struct {
|
||||||
|
model layout.ResizingModel
|
||||||
|
statusMessage string
|
||||||
|
pendingInput *startPromptMsg
|
||||||
|
textInput textinput.Model
|
||||||
|
width int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(model layout.ResizingModel, initialMsg string) StatusAndPrompt {
|
||||||
|
textInput := textinput.New()
|
||||||
|
return StatusAndPrompt{model: model, statusMessage: initialMsg, textInput: textInput}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StatusAndPrompt) Init() tea.Cmd {
|
||||||
|
return s.model.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case setStatusMsg:
|
||||||
|
s.statusMessage = string(msg)
|
||||||
|
case startPromptMsg:
|
||||||
|
if s.pendingInput != nil {
|
||||||
|
// ignore, already in an input
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.textInput.Prompt = msg.prompt
|
||||||
|
s.textInput.Focus()
|
||||||
|
s.textInput.SetValue("")
|
||||||
|
s.pendingInput = &msg
|
||||||
|
return s, nil
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if s.pendingInput != nil {
|
||||||
|
switch msg.String() {
|
||||||
|
case "ctrl+c", "esc":
|
||||||
|
s.pendingInput = nil
|
||||||
|
case "enter":
|
||||||
|
pendingInput := s.pendingInput
|
||||||
|
s.pendingInput = nil
|
||||||
|
|
||||||
|
return s, pendingInput.onDone(s.textInput.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.pendingInput != nil {
|
||||||
|
var cc utils.CmdCollector
|
||||||
|
|
||||||
|
newTextInput, cmd := s.textInput.Update(msg)
|
||||||
|
cc.Add(cmd)
|
||||||
|
s.textInput = newTextInput
|
||||||
|
|
||||||
|
if _, isKey := msg.(tea.Key); !isKey {
|
||||||
|
s.model = cc.Collect(s.model.Update(msg)).(layout.ResizingModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, cc.Cmd()
|
||||||
|
}
|
||||||
|
|
||||||
|
newModel, cmd := s.model.Update(msg)
|
||||||
|
s.model = newModel.(layout.ResizingModel)
|
||||||
|
return s, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StatusAndPrompt) View() string {
|
||||||
|
return lipgloss.JoinVertical(lipgloss.Top, s.model.View(), s.viewStatus())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StatusAndPrompt) Resize(w, h int) layout.ResizingModel {
|
||||||
|
s.width = w
|
||||||
|
submodelHeight := h - lipgloss.Height(s.viewStatus())
|
||||||
|
s.model = s.model.Resize(w, submodelHeight)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s StatusAndPrompt) viewStatus() string {
|
||||||
|
if s.pendingInput != nil {
|
||||||
|
return s.textInput.View()
|
||||||
|
}
|
||||||
|
return s.statusMessage
|
||||||
|
}
|
|
@ -6,6 +6,12 @@ type CmdCollector struct {
|
||||||
cmds []tea.Cmd
|
cmds []tea.Cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CmdCollector) Add(cmd tea.Cmd) {
|
||||||
|
if cmd != nil {
|
||||||
|
c.cmds = append(c.cmds, cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CmdCollector) Collect(m tea.Model, cmd tea.Cmd) tea.Model {
|
func (c *CmdCollector) Collect(m tea.Model, cmd tea.Cmd) tea.Model {
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
c.cmds = append(c.cmds, cmd)
|
c.cmds = append(c.cmds, cmd)
|
||||||
|
|
Loading…
Reference in a new issue