Added mode line
Also rescanning will maintain the current query
This commit is contained in:
parent
54fab1b1c3
commit
809f9adfea
|
@ -43,3 +43,8 @@ func Confirm(prompt string, onYes func() tea.Cmd) tea.Cmd {
|
||||||
type MessageWithStatus interface {
|
type MessageWithStatus interface {
|
||||||
StatusMessage() string
|
StatusMessage() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageWithMode interface {
|
||||||
|
MessageWithStatus
|
||||||
|
ModeMessage() string
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ type ErrorMsg error
|
||||||
// Message indicates that a message should be shown to the user
|
// Message indicates that a message should be shown to the user
|
||||||
type StatusMsg string
|
type StatusMsg string
|
||||||
|
|
||||||
|
// ModeMessage indicates that the mode should be changed to the following
|
||||||
|
type ModeMessage string
|
||||||
|
|
||||||
// PromptForInput indicates that the context is requesting a line of input
|
// PromptForInput indicates that the context is requesting a line of input
|
||||||
type PromptForInputMsg struct {
|
type PromptForInputMsg struct {
|
||||||
Prompt string
|
Prompt string
|
||||||
|
|
|
@ -1,18 +1,43 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewResultSet struct {
|
type NewResultSet struct {
|
||||||
ResultSet *models.ResultSet
|
ResultSet *models.ResultSet
|
||||||
|
currentFilter string
|
||||||
|
filteredCount int
|
||||||
statusMessage string
|
statusMessage string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs NewResultSet) ModeMessage() string {
|
||||||
|
var modeLine string
|
||||||
|
|
||||||
|
if rs.ResultSet.Query != nil {
|
||||||
|
modeLine = rs.ResultSet.Query.String()
|
||||||
|
} else {
|
||||||
|
modeLine = "All results"
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.currentFilter != "" {
|
||||||
|
modeLine = fmt.Sprintf("%v - Filter: '%v'", modeLine, rs.currentFilter)
|
||||||
|
}
|
||||||
|
return modeLine
|
||||||
|
}
|
||||||
|
|
||||||
func (rs NewResultSet) StatusMessage() string {
|
func (rs NewResultSet) StatusMessage() string {
|
||||||
//return fmt.Sprintf("%d items returned", len(rs.ResultSet.Items()))
|
if rs.statusMessage != "" {
|
||||||
return rs.statusMessage
|
return rs.statusMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.currentFilter != "" {
|
||||||
|
return fmt.Sprintf("%d of %d items returned", rs.filteredCount, len(rs.ResultSet.Items()))
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%d items returned", len(rs.ResultSet.Items()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetReadWrite struct {
|
type SetReadWrite struct {
|
||||||
|
|
|
@ -10,5 +10,5 @@ type TableReadService interface {
|
||||||
Describe(ctx context.Context, table string) (*models.TableInfo, error)
|
Describe(ctx context.Context, table string) (*models.TableInfo, error)
|
||||||
Scan(ctx context.Context, tableInfo *models.TableInfo) (*models.ResultSet, error)
|
Scan(ctx context.Context, tableInfo *models.TableInfo) (*models.ResultSet, error)
|
||||||
Filter(resultSet *models.ResultSet, filter string) *models.ResultSet
|
Filter(resultSet *models.ResultSet, filter string) *models.ResultSet
|
||||||
ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, queryExpr string) (*models.ResultSet, error)
|
ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, query models.Queryable) (*models.ResultSet, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,3 +44,19 @@ func (s *State) setResultSetAndFilter(resultSet *models.ResultSet, filter string
|
||||||
s.resultSet = resultSet
|
s.resultSet = resultSet
|
||||||
s.filter = filter
|
s.filter = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) buildNewResultSetMessage(statusMessage string) NewResultSet {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
var filteredCount int = 0
|
||||||
|
if s.filter != "" {
|
||||||
|
for i := range s.resultSet.Items() {
|
||||||
|
if !s.resultSet.Hidden(i) {
|
||||||
|
filteredCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewResultSet{s.resultSet, s.filter, filteredCount, statusMessage}
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"fmt"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/lmika/awstools/internal/common/ui/events"
|
"github.com/lmika/awstools/internal/common/ui/events"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
"github.com/lmika/awstools/internal/dynamo-browse/models"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/models/queryexpr"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -81,12 +81,20 @@ func (c *TableReadController) PromptForQuery() tea.Cmd {
|
||||||
Prompt: "query: ",
|
Prompt: "query: ",
|
||||||
OnDone: func(value string) tea.Cmd {
|
OnDone: func(value string) tea.Cmd {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
return c.Rescan()
|
return func() tea.Msg {
|
||||||
|
resultSet := c.state.ResultSet()
|
||||||
|
return c.doScan(context.Background(), resultSet, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr, err := queryexpr.Parse(value)
|
||||||
|
if err != nil {
|
||||||
|
return events.SetError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
resultSet := c.state.ResultSet()
|
resultSet := c.state.ResultSet()
|
||||||
newResultSet, err := c.tableService.ScanOrQuery(context.Background(), resultSet.TableInfo, value)
|
newResultSet, err := c.tableService.ScanOrQuery(context.Background(), resultSet.TableInfo, expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +108,8 @@ func (c *TableReadController) PromptForQuery() tea.Cmd {
|
||||||
|
|
||||||
func (c *TableReadController) Rescan() tea.Cmd {
|
func (c *TableReadController) Rescan() tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
return c.doScan(context.Background(), c.state.ResultSet())
|
resultSet := c.state.ResultSet()
|
||||||
|
return c.doScan(context.Background(), resultSet, resultSet.Query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,8 +148,8 @@ func (c *TableReadController) ExportCSV(filename string) tea.Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) doScan(ctx context.Context, resultSet *models.ResultSet) tea.Msg {
|
func (c *TableReadController) doScan(ctx context.Context, resultSet *models.ResultSet, query models.Queryable) tea.Msg {
|
||||||
newResultSet, err := c.tableService.Scan(ctx, resultSet.TableInfo)
|
newResultSet, err := c.tableService.ScanOrQuery(ctx, resultSet.TableInfo, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -152,21 +161,7 @@ func (c *TableReadController) doScan(ctx context.Context, resultSet *models.Resu
|
||||||
|
|
||||||
func (c *TableReadController) setResultSetAndFilter(resultSet *models.ResultSet, filter string) tea.Msg {
|
func (c *TableReadController) setResultSetAndFilter(resultSet *models.ResultSet, filter string) tea.Msg {
|
||||||
c.state.setResultSetAndFilter(resultSet, filter)
|
c.state.setResultSetAndFilter(resultSet, filter)
|
||||||
|
return c.state.buildNewResultSetMessage("")
|
||||||
var statusMessage string
|
|
||||||
if filter != "" {
|
|
||||||
var filteredCount int
|
|
||||||
for i := range resultSet.Items() {
|
|
||||||
if !resultSet.Hidden(i) {
|
|
||||||
filteredCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
statusMessage = fmt.Sprintf("%d of %d items returned", filteredCount, len(resultSet.Items()))
|
|
||||||
} else {
|
|
||||||
statusMessage = fmt.Sprintf("%d items returned", len(resultSet.Items()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewResultSet{resultSet, statusMessage}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) Unmark() tea.Cmd {
|
func (c *TableReadController) Unmark() tea.Cmd {
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (twc *TableWriteController) NewItem() tea.Cmd {
|
||||||
Dirty: true,
|
Dirty: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return NewResultSet{twc.state.ResultSet(), "New item added"}
|
return twc.state.buildNewResultSetMessage("New item added")
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyPrompts.next()
|
return keyPrompts.next()
|
||||||
|
@ -161,7 +161,7 @@ func (twc *TableWriteController) NoisyTouchItem(idx int) tea.Cmd {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return twc.tableReadControllers.doScan(ctx, resultSet)
|
return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ func (twc *TableWriteController) DeleteMarked() tea.Cmd {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return twc.tableReadControllers.doScan(ctx, resultSet)
|
return twc.tableReadControllers.doScan(ctx, resultSet, resultSet.Query)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,17 @@ package models
|
||||||
|
|
||||||
type ResultSet struct {
|
type ResultSet struct {
|
||||||
TableInfo *TableInfo
|
TableInfo *TableInfo
|
||||||
|
Query Queryable
|
||||||
Columns []string
|
Columns []string
|
||||||
items []Item
|
items []Item
|
||||||
attributes []ItemAttribute
|
attributes []ItemAttribute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Queryable interface {
|
||||||
|
String() string
|
||||||
|
Plan(tableInfo *TableInfo) (*QueryExecutionPlan, error)
|
||||||
|
}
|
||||||
|
|
||||||
type ItemAttribute struct {
|
type ItemAttribute struct {
|
||||||
Marked bool
|
Marked bool
|
||||||
Hidden bool
|
Hidden bool
|
||||||
|
|
|
@ -16,7 +16,7 @@ type astBinOp struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type astLiteralValue struct {
|
type astLiteralValue struct {
|
||||||
String string `parser:"@String"`
|
StringVal string `parser:"@String"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser = participle.MustBuild(&astExpr{})
|
var parser = participle.MustBuild(&astExpr{})
|
||||||
|
|
|
@ -6,6 +6,10 @@ type QueryExpr struct {
|
||||||
ast *astExpr
|
ast *astExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *QueryExpr) BuildQuery(tableInfo *models.TableInfo) (*models.QueryExecutionPlan, error) {
|
func (md *QueryExpr) Plan(tableInfo *models.TableInfo) (*models.QueryExecutionPlan, error) {
|
||||||
return md.ast.calcQuery(tableInfo)
|
return md.ast.calcQuery(tableInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (md *QueryExpr) String() string {
|
||||||
|
return md.ast.String()
|
||||||
|
}
|
||||||
|
|
13
internal/dynamo-browse/models/queryexpr/tostr.go
Normal file
13
internal/dynamo-browse/models/queryexpr/tostr.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package queryexpr
|
||||||
|
|
||||||
|
func (a *astExpr) String() string {
|
||||||
|
return a.Equality.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astBinOp) String() string {
|
||||||
|
return a.Name + a.Op + a.Value.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *astLiteralValue) String() string {
|
||||||
|
return a.StringVal
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *astLiteralValue) dynamoValue() (types.AttributeValue, error) {
|
func (a *astLiteralValue) dynamoValue() (types.AttributeValue, error) {
|
||||||
s, err := strconv.Unquote(a.String)
|
s, err := strconv.Unquote(a.StringVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "cannot unquote string")
|
return nil, errors.Wrap(err, "cannot unquote string")
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ func (a *astLiteralValue) dynamoValue() (types.AttributeValue, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *astLiteralValue) goValue() (any, error) {
|
func (a *astLiteralValue) goValue() (any, error) {
|
||||||
s, err := strconv.Unquote(a.String)
|
s, err := strconv.Unquote(a.StringVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "cannot unquote string")
|
return nil, errors.Wrap(err, "cannot unquote string")
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package tables
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
|
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models/queryexpr"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -33,7 +32,23 @@ func (s *Service) Scan(ctx context.Context, tableInfo *models.TableInfo) (*model
|
||||||
return s.doScan(ctx, tableInfo, nil)
|
return s.doScan(ctx, tableInfo, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, filterExpr *expression.Expression) (*models.ResultSet, error) {
|
func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, expr models.Queryable) (*models.ResultSet, error) {
|
||||||
|
var filterExpr *expression.Expression
|
||||||
|
|
||||||
|
if expr != nil {
|
||||||
|
plan, err := expr.Plan(tableInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMP
|
||||||
|
if plan.CanQuery {
|
||||||
|
return nil, errors.Errorf("queries not yet supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
filterExpr = &plan.Expression
|
||||||
|
}
|
||||||
|
|
||||||
results, err := s.provider.ScanItems(ctx, tableInfo.Name, filterExpr, 1000)
|
results, err := s.provider.ScanItems(ctx, tableInfo.Name, filterExpr, 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "unable to scan table %v", tableInfo.Name)
|
return nil, errors.Wrapf(err, "unable to scan table %v", tableInfo.Name)
|
||||||
|
@ -76,6 +91,7 @@ func (s *Service) doScan(ctx context.Context, tableInfo *models.TableInfo, filte
|
||||||
|
|
||||||
resultSet := &models.ResultSet{
|
resultSet := &models.ResultSet{
|
||||||
TableInfo: tableInfo,
|
TableInfo: tableInfo,
|
||||||
|
Query: expr,
|
||||||
Columns: columns,
|
Columns: columns,
|
||||||
}
|
}
|
||||||
resultSet.SetItems(results)
|
resultSet.SetItems(results)
|
||||||
|
@ -107,23 +123,8 @@ func (s *Service) Delete(ctx context.Context, tableInfo *models.TableInfo, items
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, queryExpr string) (*models.ResultSet, error) {
|
func (s *Service) ScanOrQuery(ctx context.Context, tableInfo *models.TableInfo, expr models.Queryable) (*models.ResultSet, error) {
|
||||||
expr, err := queryexpr.Parse(queryExpr)
|
return s.doScan(ctx, tableInfo, expr)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
plan, err := expr.BuildQuery(tableInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TEMP
|
|
||||||
if plan.CanQuery {
|
|
||||||
return nil, errors.Errorf("queries not yet supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.doScan(ctx, tableInfo, &plan.Expression)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move into a new service
|
// TODO: move into a new service
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamotableview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamotableview"
|
||||||
"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/statusandprompt"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/styles"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/tableselect"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -25,10 +26,12 @@ type Model struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteController, cc *commandctrl.CommandController) Model {
|
func NewModel(rc *controllers.TableReadController, wc *controllers.TableWriteController, cc *commandctrl.CommandController) Model {
|
||||||
dtv := dynamotableview.New()
|
uiStyles := styles.DefaultStyles
|
||||||
div := dynamoitemview.New()
|
|
||||||
statusAndPrompt := statusandprompt.New(layout.NewVBox(layout.LastChildFixedAt(17), dtv, div), "")
|
dtv := dynamotableview.New(uiStyles)
|
||||||
tableSelect := tableselect.New(statusAndPrompt)
|
div := dynamoitemview.New(uiStyles)
|
||||||
|
statusAndPrompt := statusandprompt.New(layout.NewVBox(layout.LastChildFixedAt(17), dtv, div), "", uiStyles.StatusAndPrompt)
|
||||||
|
tableSelect := tableselect.New(statusAndPrompt, uiStyles)
|
||||||
|
|
||||||
cc.AddCommands(&commandctrl.CommandContext{
|
cc.AddCommands(&commandctrl.CommandContext{
|
||||||
Commands: map[string]commandctrl.Command{
|
Commands: map[string]commandctrl.Command{
|
||||||
|
|
|
@ -3,6 +3,7 @@ package dynamoitemview
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/models/itemrender"
|
"github.com/lmika/awstools/internal/dynamo-browse/models/itemrender"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/styles"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
@ -38,9 +39,9 @@ type Model struct {
|
||||||
selectedItem models.Item
|
selectedItem models.Item
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Model {
|
func New(uiStyles styles.Styles) *Model {
|
||||||
return &Model{
|
return &Model{
|
||||||
frameTitle: frame.NewFrameTitle("Item", false, activeHeaderStyle),
|
frameTitle: frame.NewFrameTitle("Item", false, uiStyles.Frames),
|
||||||
viewport: viewport.New(100, 100),
|
viewport: viewport.New(100, 100),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/dynamoitemview"
|
||||||
"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/styles"
|
||||||
table "github.com/lmika/go-bubble-table"
|
table "github.com/lmika/go-bubble-table"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,12 +55,12 @@ func (cm columnModel) Header(index int) string {
|
||||||
return cm.m.resultSet.Columns[cm.m.colOffset+index]
|
return cm.m.resultSet.Columns[cm.m.colOffset+index]
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Model {
|
func New(uiStyles styles.Styles) *Model {
|
||||||
tbl := table.New(table.SimpleColumns([]string{"pk", "sk"}), 100, 100)
|
tbl := table.New(table.SimpleColumns([]string{"pk", "sk"}), 100, 100)
|
||||||
rows := make([]table.Row, 0)
|
rows := make([]table.Row, 0)
|
||||||
tbl.SetRows(rows)
|
tbl.SetRows(rows)
|
||||||
|
|
||||||
frameTitle := frame.NewFrameTitle("No table", true, activeHeaderStyle)
|
frameTitle := frame.NewFrameTitle("No table", true, uiStyles.Frames)
|
||||||
|
|
||||||
return &Model{
|
return &Model{
|
||||||
frameTitle: frameTitle,
|
frameTitle: frameTitle,
|
||||||
|
|
|
@ -15,14 +15,19 @@ var (
|
||||||
|
|
||||||
// Frame is a frame that appears in the
|
// Frame is a frame that appears in the
|
||||||
type FrameTitle struct {
|
type FrameTitle struct {
|
||||||
header string
|
header string
|
||||||
active bool
|
active bool
|
||||||
activeStyle lipgloss.Style
|
style Style
|
||||||
width int
|
width int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFrameTitle(header string, active bool, activeStyle lipgloss.Style) FrameTitle {
|
type Style struct {
|
||||||
return FrameTitle{header, active, activeStyle, 0}
|
ActiveTitle lipgloss.Style
|
||||||
|
InactiveTitle lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFrameTitle(header string, active bool, style Style) FrameTitle {
|
||||||
|
return FrameTitle{header, active, style, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FrameTitle) SetTitle(title string) {
|
func (f *FrameTitle) SetTitle(title string) {
|
||||||
|
@ -42,9 +47,9 @@ func (f FrameTitle) HeaderHeight() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FrameTitle) headerView() string {
|
func (f FrameTitle) headerView() string {
|
||||||
style := inactiveHeaderStyle
|
style := f.style.InactiveTitle
|
||||||
if f.active {
|
if f.active {
|
||||||
style = f.activeStyle
|
style = f.style.ActiveTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
titleText := f.header
|
titleText := f.header
|
||||||
|
|
|
@ -12,15 +12,21 @@ import (
|
||||||
// event is received, focus will be torn away and the user will be given a prompt the enter text.
|
// event is received, focus will be torn away and the user will be given a prompt the enter text.
|
||||||
type StatusAndPrompt struct {
|
type StatusAndPrompt struct {
|
||||||
model layout.ResizingModel
|
model layout.ResizingModel
|
||||||
|
style Style
|
||||||
|
modeLine string
|
||||||
statusMessage string
|
statusMessage string
|
||||||
pendingInput *events.PromptForInputMsg
|
pendingInput *events.PromptForInputMsg
|
||||||
textInput textinput.Model
|
textInput textinput.Model
|
||||||
width int
|
width int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(model layout.ResizingModel, initialMsg string) *StatusAndPrompt {
|
type Style struct {
|
||||||
|
ModeLine lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(model layout.ResizingModel, initialMsg string, style Style) *StatusAndPrompt {
|
||||||
textInput := textinput.New()
|
textInput := textinput.New()
|
||||||
return &StatusAndPrompt{model: model, statusMessage: initialMsg, textInput: textInput}
|
return &StatusAndPrompt{model: model, style: style, statusMessage: initialMsg, modeLine: "", textInput: textInput}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusAndPrompt) Init() tea.Cmd {
|
func (s *StatusAndPrompt) Init() tea.Cmd {
|
||||||
|
@ -33,7 +39,12 @@ func (s *StatusAndPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
s.statusMessage = "Error: " + msg.Error()
|
s.statusMessage = "Error: " + msg.Error()
|
||||||
case events.StatusMsg:
|
case events.StatusMsg:
|
||||||
s.statusMessage = string(msg)
|
s.statusMessage = string(msg)
|
||||||
|
case events.ModeMessage:
|
||||||
|
s.modeLine = string(msg)
|
||||||
case events.MessageWithStatus:
|
case events.MessageWithStatus:
|
||||||
|
if hasModeMessage, ok := msg.(events.MessageWithMode); ok {
|
||||||
|
s.modeLine = hasModeMessage.ModeMessage()
|
||||||
|
}
|
||||||
s.statusMessage = msg.StatusMessage()
|
s.statusMessage = msg.StatusMessage()
|
||||||
case events.PromptForInputMsg:
|
case events.PromptForInputMsg:
|
||||||
if s.pendingInput != nil {
|
if s.pendingInput != nil {
|
||||||
|
@ -87,8 +98,14 @@ func (s *StatusAndPrompt) Resize(w, h int) layout.ResizingModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StatusAndPrompt) viewStatus() string {
|
func (s *StatusAndPrompt) viewStatus() string {
|
||||||
|
modeLine := s.style.ModeLine.Render(lipgloss.PlaceHorizontal(s.width, lipgloss.Left, s.modeLine, lipgloss.WithWhitespaceChars(" ")))
|
||||||
|
|
||||||
|
var statusLine string
|
||||||
if s.pendingInput != nil {
|
if s.pendingInput != nil {
|
||||||
return s.textInput.View()
|
statusLine = s.textInput.View()
|
||||||
|
} else {
|
||||||
|
statusLine = s.statusMessage
|
||||||
}
|
}
|
||||||
return s.statusMessage
|
|
||||||
|
return lipgloss.JoinVertical(lipgloss.Top, modeLine, statusLine)
|
||||||
}
|
}
|
||||||
|
|
29
internal/dynamo-browse/ui/teamodels/styles/styles.go
Normal file
29
internal/dynamo-browse/ui/teamodels/styles/styles.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package styles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/frame"
|
||||||
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/statusandprompt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Styles struct {
|
||||||
|
Frames frame.Style
|
||||||
|
StatusAndPrompt statusandprompt.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultStyles = Styles{
|
||||||
|
Frames: frame.Style{
|
||||||
|
ActiveTitle: lipgloss.NewStyle().
|
||||||
|
Bold(true).
|
||||||
|
Foreground(lipgloss.Color("#ffffff")).
|
||||||
|
Background(lipgloss.Color("#4479ff")),
|
||||||
|
InactiveTitle: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#d1d1d1")),
|
||||||
|
},
|
||||||
|
StatusAndPrompt: statusandprompt.Style{
|
||||||
|
ModeLine: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#d1d1d1")),
|
||||||
|
},
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
"github.com/lmika/awstools/internal/dynamo-browse/controllers"
|
||||||
"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/styles"
|
||||||
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
|
"github.com/lmika/awstools/internal/dynamo-browse/ui/teamodels/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,8 +27,8 @@ type Model struct {
|
||||||
w, h int
|
w, h int
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(submodel tea.Model) *Model {
|
func New(submodel tea.Model, uiStyles styles.Styles) *Model {
|
||||||
frameTitle := frame.NewFrameTitle("Select table", false, activeHeaderStyle)
|
frameTitle := frame.NewFrameTitle("Select table", false, uiStyles.Frames)
|
||||||
return &Model{frameTitle: frameTitle, submodel: submodel}
|
return &Model{frameTitle: frameTitle, submodel: submodel}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue