Have got a modal table-selection list working
Also tracked down what was causing major pauses when creating new tables. It was due to querying whether terminal is light or not. So making a call to get that info on launch.
This commit is contained in:
parent
81cd1d0971
commit
5d213c4ee8
|
@ -7,6 +7,7 @@ import (
|
||||||
"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"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
"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"
|
||||||
"github.com/lmika/awstools/internal/common/ui/uimodels"
|
"github.com/lmika/awstools/internal/common/ui/uimodels"
|
||||||
|
@ -19,6 +20,7 @@ import (
|
||||||
"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/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
|
||||||
"github.com/lmika/gopkgs/cli"
|
"github.com/lmika/gopkgs/cli"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -65,13 +67,17 @@ func main() {
|
||||||
_ = uiModel
|
_ = uiModel
|
||||||
// END TEMP
|
// END TEMP
|
||||||
|
|
||||||
model := layout.FullScreen(statusandprompt.New(
|
var model tea.Model = statusandprompt.New(
|
||||||
layout.NewVBox(
|
layout.NewVBox(
|
||||||
frame.NewFrame("This is the header", true, layout.Model(newTestModel("this is the top"))),
|
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"))),
|
frame.NewFrame("This is another header", false, layout.Model(newTestModel("this is the bottom"))),
|
||||||
),
|
),
|
||||||
"Hello world",
|
"Hello world",
|
||||||
))
|
)
|
||||||
|
model = layout.FullScreen(tableselect.New(model))
|
||||||
|
|
||||||
|
// Pre-determine if layout has dark background. This prevents calls for creating a list to hang.
|
||||||
|
lipgloss.HasDarkBackground()
|
||||||
|
|
||||||
//frameSet := frameset.New([]frameset.Frame{
|
//frameSet := frameset.New([]frameset.Frame{
|
||||||
// {
|
// {
|
||||||
|
@ -129,8 +135,8 @@ 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 statusandprompt.Prompt("What is your car? ", func(val string) tea.Cmd {
|
return tableselect.ShowTableSelect(func(n string) tea.Cmd {
|
||||||
return statusandprompt.SetStatus("Your car is = " + val)
|
return statusandprompt.SetStatus("New table = " + n)
|
||||||
})
|
})
|
||||||
} else if k == "k" {
|
} else if k == "k" {
|
||||||
return modal.PopMode
|
return modal.PopMode
|
||||||
|
|
|
@ -15,6 +15,15 @@ type ResizingModel interface {
|
||||||
Resize(w, h int) ResizingModel
|
Resize(w, h int) ResizingModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize sends a resize message to the passed in model. If m implements ResizingModel, then Resize is called;
|
||||||
|
// otherwise, m is returned without any messages.
|
||||||
|
func Resize(m tea.Model, w, h int) tea.Model {
|
||||||
|
if rm, isRm := m.(ResizingModel); isRm {
|
||||||
|
return rm.Resize(w, h)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
// Model takes a tea-model and displays it as a resizing model. The model will be
|
// Model takes a tea-model and displays it as a resizing model. The model will be
|
||||||
// displayed with all the available space provided
|
// displayed with all the available space provided
|
||||||
func Model(m tea.Model) ResizingModel {
|
func Model(m tea.Model) ResizingModel {
|
||||||
|
@ -46,3 +55,54 @@ func (t teaModel) Resize(w, h int) ResizingModel {
|
||||||
t.w, t.h = w, h
|
t.w, t.h = w, h
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResizableModelHandler struct {
|
||||||
|
new func(w, h int) tea.Model
|
||||||
|
resize func(m tea.Model, w, h int) tea.Model
|
||||||
|
model tea.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResizableModelHandler takes a tea model that requires a with and height during construction
|
||||||
|
// and has a resize method, and wraps it as a resizing model.
|
||||||
|
func NewResizableModelHandler(newModel func(w, h int) tea.Model) ResizableModelHandler {
|
||||||
|
return ResizableModelHandler{
|
||||||
|
new: newModel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rmh ResizableModelHandler) WithResize(resizeFn func(m tea.Model, w, h int) tea.Model) ResizableModelHandler {
|
||||||
|
rmh.resize = resizeFn
|
||||||
|
return rmh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rmh ResizableModelHandler) Resize(w, h int) ResizingModel {
|
||||||
|
if rmh.model == nil {
|
||||||
|
rmh.model = rmh.new(w, h)
|
||||||
|
// TODO: handle init
|
||||||
|
} else if rmh.resize != nil {
|
||||||
|
rmh.model = rmh.resize(rmh.model, w, h)
|
||||||
|
}
|
||||||
|
return rmh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rmh ResizableModelHandler) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rmh ResizableModelHandler) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if rmh.model == nil {
|
||||||
|
return rmh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newModel, cmd := rmh.model.Update(msg)
|
||||||
|
rmh.model = newModel
|
||||||
|
return rmh, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rmh ResizableModelHandler) View() string {
|
||||||
|
if rmh.model == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return rmh.model.View()
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package modal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
@ -18,30 +19,34 @@ func New(baseMode tea.Model) Modal {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Modal) Init() tea.Cmd {
|
func (m Modal) Init() tea.Cmd {
|
||||||
return nil
|
return m.baseMode.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Modal) pushMode(model tea.Model) {
|
// Push pushes a new model onto the modal stack
|
||||||
|
func (m *Modal) Push(model tea.Model) {
|
||||||
m.modeStack = append(m.modeStack, model)
|
m.modeStack = append(m.modeStack, model)
|
||||||
log.Printf("pusing new mode: len = %v", len(m.modeStack))
|
log.Printf("pusing new mode: len = %v", len(m.modeStack))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Modal) popMode() {
|
// Pop pops a model from the stack
|
||||||
|
func (m *Modal) Pop() (p tea.Model) {
|
||||||
if len(m.modeStack) > 0 {
|
if len(m.modeStack) > 0 {
|
||||||
|
p = m.modeStack[len(m.modeStack)-1]
|
||||||
m.modeStack = m.modeStack[:len(m.modeStack)-1]
|
m.modeStack = m.modeStack[:len(m.modeStack)-1]
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of models on the mode stack
|
||||||
|
func (m Modal) Len() int {
|
||||||
|
return len(m.modeStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Modal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m Modal) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cc utils.CmdCollector
|
var cc utils.CmdCollector
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case newModePushed:
|
|
||||||
m.pushMode(msg)
|
|
||||||
return m, nil
|
|
||||||
case modePopped:
|
|
||||||
m.popMode()
|
|
||||||
return m, nil
|
|
||||||
case tea.KeyMsg, tea.MouseMsg:
|
case tea.KeyMsg, tea.MouseMsg:
|
||||||
// only notify top level stack
|
// only notify top level stack
|
||||||
if len(m.modeStack) > 0 {
|
if len(m.modeStack) > 0 {
|
||||||
|
@ -69,3 +74,11 @@ func (m Modal) View() string {
|
||||||
}
|
}
|
||||||
return m.baseMode.View()
|
return m.baseMode.View()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Modal) Resize(w, h int) layout.ResizingModel {
|
||||||
|
m.baseMode = layout.Resize(m.baseMode, w, h)
|
||||||
|
for i := range m.modeStack {
|
||||||
|
m.modeStack[i] = layout.Resize(m.modeStack[i], w, h)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
15
internal/dynamo-browse/ui/teamodels/tableselect/events.go
Normal file
15
internal/dynamo-browse/ui/teamodels/tableselect/events.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package tableselect
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
func ShowTableSelect(onSelected func(n string) tea.Cmd) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
return showTableSelectMsg{
|
||||||
|
onSelected: onSelected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type showTableSelectMsg struct {
|
||||||
|
onSelected func(n string) tea.Cmd
|
||||||
|
}
|
27
internal/dynamo-browse/ui/teamodels/tableselect/items.go
Normal file
27
internal/dynamo-browse/ui/teamodels/tableselect/items.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package tableselect
|
||||||
|
|
||||||
|
import "github.com/charmbracelet/bubbles/list"
|
||||||
|
|
||||||
|
type tableItem struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti tableItem) FilterValue() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti tableItem) Title() string {
|
||||||
|
return ti.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ti tableItem) Description() string {
|
||||||
|
return "abc"
|
||||||
|
}
|
||||||
|
|
||||||
|
func toListItems[T list.Item](xs []T) []list.Item {
|
||||||
|
ls := make([]list.Item, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
ls[i] = x
|
||||||
|
}
|
||||||
|
return ls
|
||||||
|
}
|
55
internal/dynamo-browse/ui/teamodels/tableselect/list.go
Normal file
55
internal/dynamo-browse/ui/teamodels/tableselect/list.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package tableselect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charmbracelet/bubbles/list"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
titleStyle = lipgloss.NewStyle().MarginLeft(2)
|
||||||
|
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
|
||||||
|
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
|
||||||
|
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
|
||||||
|
helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
|
||||||
|
quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
type listController struct {
|
||||||
|
list list.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListController(w, h int) listController {
|
||||||
|
tableItems := []tableItem{
|
||||||
|
{name: "alpha"},
|
||||||
|
{name: "beta"},
|
||||||
|
{name: "gamma"},
|
||||||
|
}
|
||||||
|
|
||||||
|
items := toListItems(tableItems)
|
||||||
|
|
||||||
|
delegate := list.NewDefaultDelegate()
|
||||||
|
delegate.ShowDescription = false
|
||||||
|
|
||||||
|
return listController{list.New(items, delegate, w, h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l listController) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l listController) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
newList, cmd := l.list.Update(msg)
|
||||||
|
l.list = newList
|
||||||
|
return l, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l listController) View() string {
|
||||||
|
return l.list.View()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l listController) Resize(w, h int) layout.ResizingModel {
|
||||||
|
l.list.SetSize(w, h)
|
||||||
|
return l
|
||||||
|
}
|
56
internal/dynamo-browse/ui/teamodels/tableselect/model.go
Normal file
56
internal/dynamo-browse/ui/teamodels/tableselect/model.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package tableselect
|
||||||
|
|
||||||
|
import (
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/modal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
pendingSelection *showTableSelectMsg
|
||||||
|
modal modal.Modal
|
||||||
|
w, h int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(submodel tea.Model) Model {
|
||||||
|
return Model{modal: modal.New(submodel)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) Init() tea.Cmd {
|
||||||
|
return m.modal.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case showTableSelectMsg:
|
||||||
|
m.pendingSelection = &msg
|
||||||
|
m.modal.Push(newListController(m.w, m.h))
|
||||||
|
return m, nil
|
||||||
|
case tea.KeyMsg:
|
||||||
|
if m.modal.Len() > 0 {
|
||||||
|
switch msg.String() {
|
||||||
|
case "enter":
|
||||||
|
listController := m.modal.Pop().(listController)
|
||||||
|
|
||||||
|
var sel showTableSelectMsg
|
||||||
|
sel, m.pendingSelection = *m.pendingSelection, nil
|
||||||
|
|
||||||
|
return m, sel.onSelected(listController.list.SelectedItem().(tableItem).name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newModal, cmd := m.modal.Update(msg)
|
||||||
|
m.modal = newModal.(modal.Modal)
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) View() string {
|
||||||
|
return m.modal.View()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Model) Resize(w, h int) layout.ResizingModel {
|
||||||
|
m.w, m.h = w, h
|
||||||
|
m.modal = layout.Resize(m.modal, w, h).(modal.Modal)
|
||||||
|
return m
|
||||||
|
}
|
Loading…
Reference in a new issue