Added status and prompt
This commit is contained in:
parent
b0909ffe4e
commit
81cd1d0971
|
@ -6,7 +6,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/awstools/internal/common/ui/commandctrl"
|
||||
"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/layout"
|
||||
"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"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -65,9 +65,12 @@ func main() {
|
|||
_ = uiModel
|
||||
// END TEMP
|
||||
|
||||
model := layout.FullScreen(layout.NewVBox(
|
||||
frame.NewFrame("This is the header", layout.Model(newTestModel("this is the top"))),
|
||||
frame.NewFrame("This is another header", layout.Model(newTestModel("this is the bottom"))),
|
||||
model := layout.FullScreen(statusandprompt.New(
|
||||
layout.NewVBox(
|
||||
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{
|
||||
|
@ -126,7 +129,9 @@ func newTestModel(descr string) tea.Model {
|
|||
OnKeyPressed: func(k string) tea.Cmd {
|
||||
log.Println("got key press: " + k)
|
||||
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" {
|
||||
return modal.PopMode
|
||||
}
|
||||
|
|
|
@ -13,17 +13,22 @@ var (
|
|||
Bold(true).
|
||||
Foreground(lipgloss.Color("#ffffff")).
|
||||
Background(lipgloss.Color("#4479ff"))
|
||||
|
||||
inactiveHeaderStyle = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#000000")).
|
||||
Background(lipgloss.Color("#d1d1d1"))
|
||||
)
|
||||
|
||||
// Frame is a frame that appears in the
|
||||
type Frame struct {
|
||||
header string
|
||||
active bool
|
||||
model layout.ResizingModel
|
||||
width int
|
||||
}
|
||||
|
||||
func NewFrame(header string, model layout.ResizingModel) Frame {
|
||||
return Frame{header, model, 0}
|
||||
func NewFrame(header string, active bool, model layout.ResizingModel) Frame {
|
||||
return Frame{header, active, model, 0}
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
f.model = newModel.(layout.ResizingModel)
|
||||
return f, cmd
|
||||
|
@ -48,8 +61,13 @@ func (f Frame) Resize(w, h int) layout.ResizingModel {
|
|||
}
|
||||
|
||||
func (f Frame) headerView() string {
|
||||
style := inactiveHeaderStyle
|
||||
if f.active {
|
||||
style = activeHeaderStyle
|
||||
}
|
||||
|
||||
titleText := f.header
|
||||
title := activeHeaderStyle.Render(titleText)
|
||||
line := activeHeaderStyle.Render(strings.Repeat(" ", utils.Max(0, f.width-lipgloss.Width(title))))
|
||||
title := style.Render(titleText)
|
||||
line := style.Render(strings.Repeat(" ", utils.Max(0, f.width-lipgloss.Width(title))))
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
if cmd != nil {
|
||||
c.cmds = append(c.cmds, cmd)
|
||||
|
|
Loading…
Reference in a new issue