Fixed a glaring error where the user cannot close the column selector
Some checks failed
ci / build (push) Has been cancelled
Some checks failed
ci / build (push) Has been cancelled
Cause of this was that the close event type was also being used by the related overlay, and the event was being caught by that even though the overlay was hidden. Also started working on changing the sort order within the column selector by pressing S.
This commit is contained in:
parent
f5bf31a903
commit
e37b8099a3
|
@ -123,7 +123,7 @@ func main() {
|
||||||
*flagTable,
|
*flagTable,
|
||||||
)
|
)
|
||||||
tableWriteController := controllers.NewTableWriteController(state, tableService, jobsController, tableReadController, settingStore)
|
tableWriteController := controllers.NewTableWriteController(state, tableService, jobsController, tableReadController, settingStore)
|
||||||
columnsController := controllers.NewColumnsController(eventBus)
|
columnsController := controllers.NewColumnsController(tableReadController, eventBus)
|
||||||
exportController := controllers.NewExportController(state, tableService, jobsController, columnsController, pasteboardProvider)
|
exportController := controllers.NewExportController(state, tableService, jobsController, columnsController, pasteboardProvider)
|
||||||
settingsController := controllers.NewSettingsController(settingStore, eventBus)
|
settingsController := controllers.NewSettingsController(settingStore, eventBus)
|
||||||
keyBindings := keybindings.Default()
|
keyBindings := keybindings.Default()
|
||||||
|
|
|
@ -5,19 +5,22 @@ import (
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/columns"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/columns"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/evaluators"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
||||||
bus "github.com/lmika/events"
|
bus "github.com/lmika/events"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ColumnsController struct {
|
type ColumnsController struct {
|
||||||
|
tr *TableReadController
|
||||||
|
|
||||||
// State
|
// State
|
||||||
colModel *columns.Columns
|
colModel *columns.Columns
|
||||||
resultSet *models.ResultSet
|
resultSet *models.ResultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewColumnsController(eventBus *bus.Bus) *ColumnsController {
|
func NewColumnsController(tr *TableReadController, eventBus *bus.Bus) *ColumnsController {
|
||||||
cc := &ColumnsController{}
|
cc := &ColumnsController{tr: tr}
|
||||||
|
|
||||||
eventBus.On(newResultSetEvent, cc.onNewResultSet)
|
eventBus.On(newResultSetEvent, cc.onNewResultSet)
|
||||||
return cc
|
return cc
|
||||||
|
@ -80,7 +83,7 @@ func (cc *ColumnsController) AddColumn(afterIndex int) tea.Msg {
|
||||||
|
|
||||||
newCol := columns.Column{
|
newCol := columns.Column{
|
||||||
Name: colExpr.String(),
|
Name: colExpr.String(),
|
||||||
Evaluator: columns.ExprFieldValueEvaluator{Expr: colExpr},
|
Evaluator: queryexpr.ExprFieldValueEvaluator{Expr: colExpr},
|
||||||
}
|
}
|
||||||
|
|
||||||
if afterIndex >= len(cc.colModel.Columns)-1 {
|
if afterIndex >= len(cc.colModel.Columns)-1 {
|
||||||
|
@ -117,6 +120,25 @@ func (cc *ColumnsController) DeleteColumn(afterIndex int) tea.Msg {
|
||||||
return ColumnsUpdated{}
|
return ColumnsUpdated{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ColumnsController) SortByColumn(index int) tea.Msg {
|
||||||
|
if index >= len(cc.colModel.Columns) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
column := cc.colModel.Columns[index]
|
||||||
|
newCriteria := models.SortCriteria{
|
||||||
|
Fields: []models.SortField{
|
||||||
|
{Field: column.Evaluator, Asc: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if ff := cc.SortCriteria().FirstField(); evaluators.Equals(ff.Field, column.Evaluator) {
|
||||||
|
newCriteria.Fields[0].Asc = !ff.Asc
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.SetSortCriteria(newCriteria)
|
||||||
|
return ColumnsUpdated{}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ColumnsController) AttributesWithPrefix(prefix string) []string {
|
func (c *ColumnsController) AttributesWithPrefix(prefix string) []string {
|
||||||
options := make([]string, 0)
|
options := make([]string, 0)
|
||||||
for _, col := range c.resultSet.Columns() {
|
for _, col := range c.resultSet.Columns() {
|
||||||
|
@ -126,3 +148,15 @@ func (c *ColumnsController) AttributesWithPrefix(prefix string) []string {
|
||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ColumnsController) SortCriteria() models.SortCriteria {
|
||||||
|
if cc.resultSet == nil {
|
||||||
|
return models.SortCriteria{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc.resultSet.SortCriteria()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ColumnsController) SetSortCriteria(criteria models.SortCriteria) {
|
||||||
|
cc.tr.SortResultSet(criteria)
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ const (
|
||||||
resultSetUpdateTouch
|
resultSetUpdateTouch
|
||||||
resultSetUpdateNextPage
|
resultSetUpdateNextPage
|
||||||
resultSetUpdateScript
|
resultSetUpdateScript
|
||||||
|
resultSetUpdateResort
|
||||||
)
|
)
|
||||||
|
|
||||||
type MarkOp int
|
type MarkOp int
|
||||||
|
@ -150,6 +151,13 @@ func (c *TableReadController) ScanTable(name string) tea.Msg {
|
||||||
}).OnEither(c.handleResultSetFromJobResult(c.state.Filter(), true, false, resultSetUpdateInit)).Submit()
|
}).OnEither(c.handleResultSetFromJobResult(c.state.Filter(), true, false, resultSetUpdateInit)).Submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TableReadController) SortResultSet(newCriteria models.SortCriteria) {
|
||||||
|
c.state.withResultSet(func(rs *models.ResultSet) {
|
||||||
|
rs.Sort(newCriteria.Append(models.PKSKSortFilter(rs.TableInfo)))
|
||||||
|
})
|
||||||
|
c.eventBus.Fire(newResultSetEvent, c.state.resultSet, resultSetUpdateResort)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *TableReadController) PromptForQuery() tea.Msg {
|
func (c *TableReadController) PromptForQuery() tea.Msg {
|
||||||
return events.PromptForInputMsg{
|
return events.PromptForInputMsg{
|
||||||
Prompt: "query: ",
|
Prompt: "query: ",
|
||||||
|
|
|
@ -632,7 +632,7 @@ func newService(t *testing.T, cfg serviceConfig) *services {
|
||||||
)
|
)
|
||||||
writeController := controllers.NewTableWriteController(state, service, jobsController, readController, settingStore)
|
writeController := controllers.NewTableWriteController(state, service, jobsController, readController, settingStore)
|
||||||
settingsController := controllers.NewSettingsController(settingStore, eventBus)
|
settingsController := controllers.NewSettingsController(settingStore, eventBus)
|
||||||
columnsController := controllers.NewColumnsController(eventBus)
|
columnsController := controllers.NewColumnsController(readController, eventBus)
|
||||||
exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{})
|
exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{})
|
||||||
scriptController := controllers.NewScriptController(scriptService, readController, jobsController, settingsController, eventBus)
|
scriptController := controllers.NewScriptController(scriptService, readController, jobsController, settingsController, eventBus)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package columns
|
package columns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Columns struct {
|
type Columns struct {
|
||||||
|
@ -19,7 +17,7 @@ func NewColumnsFromResultSet(rs *models.ResultSet) *Columns {
|
||||||
for i, c := range rsCols {
|
for i, c := range rsCols {
|
||||||
cols[i] = Column{
|
cols[i] = Column{
|
||||||
Name: c,
|
Name: c,
|
||||||
Evaluator: SimpleFieldValueEvaluator(c),
|
Evaluator: models.SimpleFieldValueEvaluator(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +42,7 @@ func (cols *Columns) AddMissingColumns(rs *models.ResultSet) {
|
||||||
if _, hasCol := existingColumns[c]; !hasCol {
|
if _, hasCol := existingColumns[c]; !hasCol {
|
||||||
newCols = append(newCols, Column{
|
newCols = append(newCols, Column{
|
||||||
Name: c,
|
Name: c,
|
||||||
Evaluator: SimpleFieldValueEvaluator(c),
|
Evaluator: models.SimpleFieldValueEvaluator(c),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +54,7 @@ func (cols *Columns) AddMissingColumns(rs *models.ResultSet) {
|
||||||
} else {
|
} else {
|
||||||
newCols[i] = Column{
|
newCols[i] = Column{
|
||||||
Name: c,
|
Name: c,
|
||||||
Evaluator: SimpleFieldValueEvaluator(c),
|
Evaluator: models.SimpleFieldValueEvaluator(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,25 +80,6 @@ func (cols *Columns) VisibleColumns() []Column {
|
||||||
|
|
||||||
type Column struct {
|
type Column struct {
|
||||||
Name string
|
Name string
|
||||||
Evaluator FieldValueEvaluator
|
Evaluator models.FieldValueEvaluator
|
||||||
Hidden bool
|
Hidden bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type FieldValueEvaluator interface {
|
|
||||||
EvaluateForItem(item models.Item) types.AttributeValue
|
|
||||||
}
|
|
||||||
|
|
||||||
type SimpleFieldValueEvaluator string
|
|
||||||
|
|
||||||
func (sfve SimpleFieldValueEvaluator) EvaluateForItem(item models.Item) types.AttributeValue {
|
|
||||||
return item[string(sfve)]
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExprFieldValueEvaluator struct {
|
|
||||||
Expr *queryexpr.QueryExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sfve ExprFieldValueEvaluator) EvaluateForItem(item models.Item) types.AttributeValue {
|
|
||||||
val, _ := sfve.Expr.EvalItem(item)
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
15
internal/dynamo-browse/models/evaluators.go
Normal file
15
internal/dynamo-browse/models/evaluators.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FieldValueEvaluator interface {
|
||||||
|
EvaluateForItem(item Item) types.AttributeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleFieldValueEvaluator string
|
||||||
|
|
||||||
|
func (sfve SimpleFieldValueEvaluator) EvaluateForItem(item Item) types.AttributeValue {
|
||||||
|
return item[string(sfve)]
|
||||||
|
}
|
25
internal/dynamo-browse/models/evaluators/equals.go
Normal file
25
internal/dynamo-browse/models/evaluators/equals.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package evaluators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Equals(x, y models.FieldValueEvaluator) bool {
|
||||||
|
if x == nil {
|
||||||
|
return y == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch xt := x.(type) {
|
||||||
|
case models.SimpleFieldValueEvaluator:
|
||||||
|
if yt, ok := y.(models.SimpleFieldValueEvaluator); ok {
|
||||||
|
return xt == yt
|
||||||
|
}
|
||||||
|
case queryexpr.ExprFieldValueEvaluator:
|
||||||
|
if yt, ok := y.(queryexpr.ExprFieldValueEvaluator); ok {
|
||||||
|
return xt.Expr.Equal(yt.Expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -18,7 +18,8 @@ type ResultSet struct {
|
||||||
items []Item
|
items []Item
|
||||||
attributes []ItemAttribute
|
attributes []ItemAttribute
|
||||||
|
|
||||||
columns []string
|
columns []string
|
||||||
|
sortCriteria SortCriteria
|
||||||
}
|
}
|
||||||
|
|
||||||
type Queryable interface {
|
type Queryable interface {
|
||||||
|
@ -48,6 +49,10 @@ func (rs *ResultSet) SetItems(items []Item) {
|
||||||
rs.attributes = make([]ItemAttribute, len(items))
|
rs.attributes = make([]ItemAttribute, len(items))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *ResultSet) SortCriteria() SortCriteria {
|
||||||
|
return rs.sortCriteria
|
||||||
|
}
|
||||||
|
|
||||||
func (rs *ResultSet) AddNewItem(item Item, attrs ItemAttribute) {
|
func (rs *ResultSet) AddNewItem(item Item, attrs ItemAttribute) {
|
||||||
rs.items = append(rs.items, item)
|
rs.items = append(rs.items, item)
|
||||||
rs.attributes = append(rs.attributes, attrs)
|
rs.attributes = append(rs.attributes, attrs)
|
||||||
|
@ -141,3 +146,8 @@ func (rs *ResultSet) RefreshColumns() {
|
||||||
func (rs *ResultSet) HasNextPage() bool {
|
func (rs *ResultSet) HasNextPage() bool {
|
||||||
return rs.LastEvaluatedKey != nil
|
return rs.LastEvaluatedKey != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *ResultSet) Sort(criteria SortCriteria) {
|
||||||
|
rs.sortCriteria = criteria
|
||||||
|
Sort(rs.items, criteria)
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,11 @@ func TestModExpr_Query(t *testing.T) {
|
||||||
`#0 = :0`,
|
`#0 = :0`,
|
||||||
exprNameIsString(0, 0, "pk", "prefix"),
|
exprNameIsString(0, 0, "pk", "prefix"),
|
||||||
),
|
),
|
||||||
|
//scanCase("when request pk is fixed (reverse)",
|
||||||
|
// `prefix="pk"`,
|
||||||
|
// `#0 = :0`,
|
||||||
|
// exprNameIsString(0, 0, "pk", "prefix"),
|
||||||
|
//),
|
||||||
scanCase("when request pk is fixed in parens #1",
|
scanCase("when request pk is fixed in parens #1",
|
||||||
`(pk="prefix")`,
|
`(pk="prefix")`,
|
||||||
`#0 = :0`,
|
`#0 = :0`,
|
||||||
|
@ -513,6 +518,7 @@ func TestQueryExpr_EvalItem(t *testing.T) {
|
||||||
{expr: "three <= 2", expected: &types.AttributeValueMemberBOOL{Value: false}},
|
{expr: "three <= 2", expected: &types.AttributeValueMemberBOOL{Value: false}},
|
||||||
|
|
||||||
// Between
|
// Between
|
||||||
|
{expr: "3 between 1 and 5", expected: &types.AttributeValueMemberBOOL{Value: true}},
|
||||||
{expr: "three between 1 and 5", expected: &types.AttributeValueMemberBOOL{Value: true}},
|
{expr: "three between 1 and 5", expected: &types.AttributeValueMemberBOOL{Value: true}},
|
||||||
{expr: "three between one and five", expected: &types.AttributeValueMemberBOOL{Value: true}},
|
{expr: "three between one and five", expected: &types.AttributeValueMemberBOOL{Value: true}},
|
||||||
{expr: "three between 10 and 15", expected: &types.AttributeValueMemberBOOL{Value: false}},
|
{expr: "three between 10 and 15", expected: &types.AttributeValueMemberBOOL{Value: false}},
|
||||||
|
|
15
internal/dynamo-browse/models/queryexpr/fieldevaluator.go
Normal file
15
internal/dynamo-browse/models/queryexpr/fieldevaluator.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package queryexpr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExprFieldValueEvaluator struct {
|
||||||
|
Expr *QueryExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sfve ExprFieldValueEvaluator) EvaluateForItem(item models.Item) types.AttributeValue {
|
||||||
|
val, _ := sfve.Expr.EvalItem(item)
|
||||||
|
return val
|
||||||
|
}
|
|
@ -8,13 +8,60 @@ import (
|
||||||
// sortedItems is a collection of items that is sorted.
|
// sortedItems is a collection of items that is sorted.
|
||||||
// Items are sorted based on the PK, and SK in ascending order
|
// Items are sorted based on the PK, and SK in ascending order
|
||||||
type sortedItems struct {
|
type sortedItems struct {
|
||||||
tableInfo *TableInfo
|
criteria SortCriteria
|
||||||
items []Item
|
items []Item
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortField struct {
|
||||||
|
Field FieldValueEvaluator
|
||||||
|
Asc bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortCriteria struct {
|
||||||
|
Fields []SortField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc SortCriteria) FirstField() SortField {
|
||||||
|
if len(sc.Fields) == 0 {
|
||||||
|
return SortField{}
|
||||||
|
}
|
||||||
|
return sc.Fields[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc SortCriteria) Equals(osc SortCriteria) bool {
|
||||||
|
if len(sc.Fields) != len(osc.Fields) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range osc.Fields {
|
||||||
|
if sc.Fields[i].Field != osc.Fields[i].Field ||
|
||||||
|
sc.Fields[i].Asc != osc.Fields[i].Asc {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc SortCriteria) Append(osc SortCriteria) SortCriteria {
|
||||||
|
newItems := make([]SortField, 0, len(osc.Fields))
|
||||||
|
newItems = append(newItems, sc.Fields...)
|
||||||
|
newItems = append(newItems, osc.Fields...)
|
||||||
|
return SortCriteria{Fields: newItems}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PKSKSortFilter(ti *TableInfo) SortCriteria {
|
||||||
|
return SortCriteria{
|
||||||
|
Fields: []SortField{
|
||||||
|
{Field: SimpleFieldValueEvaluator(ti.Keys.PartitionKey), Asc: true},
|
||||||
|
{Field: SimpleFieldValueEvaluator(ti.Keys.SortKey), Asc: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sorts the items in place
|
// Sort sorts the items in place
|
||||||
func Sort(items []Item, tableInfo *TableInfo) {
|
func Sort(items []Item, criteria SortCriteria) {
|
||||||
si := sortedItems{items: items, tableInfo: tableInfo}
|
si := sortedItems{items: items, criteria: criteria}
|
||||||
sort.Sort(&si)
|
sort.Sort(&si)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,30 +70,21 @@ func (si *sortedItems) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (si *sortedItems) Less(i, j int) bool {
|
func (si *sortedItems) Less(i, j int) bool {
|
||||||
// Compare primary keys
|
for _, field := range si.criteria.Fields {
|
||||||
pv1, pv2 := si.items[i][si.tableInfo.Keys.PartitionKey], si.items[j][si.tableInfo.Keys.PartitionKey]
|
// Compare primary keys
|
||||||
pc, ok := attrutils.CompareScalarAttributes(pv1, pv2)
|
pv1, pv2 := field.Field.EvaluateForItem(si.items[i]), field.Field.EvaluateForItem(si.items[j])
|
||||||
if !ok {
|
pc, ok := attrutils.CompareScalarAttributes(pv1, pv2)
|
||||||
return i < j
|
|
||||||
}
|
|
||||||
|
|
||||||
if pc < 0 {
|
|
||||||
return true
|
|
||||||
} else if pc > 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partition keys are equal, compare sort key
|
|
||||||
if sortKey := si.tableInfo.Keys.SortKey; sortKey != "" {
|
|
||||||
sv1, sv2 := si.items[i][sortKey], si.items[j][sortKey]
|
|
||||||
sc, ok := attrutils.CompareScalarAttributes(sv1, sv2)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return i < j
|
return i < j
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc < 0 {
|
if !field.Asc {
|
||||||
|
pc = -pc
|
||||||
|
}
|
||||||
|
|
||||||
|
if pc < 0 {
|
||||||
return true
|
return true
|
||||||
} else if sc > 0 {
|
} else if pc > 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ func TestSort(t *testing.T) {
|
||||||
items := make([]models.Item, len(testStringData))
|
items := make([]models.Item, len(testStringData))
|
||||||
copy(items, testStringData)
|
copy(items, testStringData)
|
||||||
|
|
||||||
models.Sort(items, tableInfo)
|
models.Sort(items, models.PKSKSortFilter(tableInfo))
|
||||||
|
|
||||||
assert.Equal(t, items[0], testStringData[1])
|
assert.Equal(t, items[0], testStringData[1])
|
||||||
assert.Equal(t, items[1], testStringData[2])
|
assert.Equal(t, items[1], testStringData[2])
|
||||||
|
@ -28,7 +28,7 @@ func TestSort(t *testing.T) {
|
||||||
items := make([]models.Item, len(testNumberData))
|
items := make([]models.Item, len(testNumberData))
|
||||||
copy(items, testNumberData)
|
copy(items, testNumberData)
|
||||||
|
|
||||||
models.Sort(items, tableInfo)
|
models.Sort(items, models.PKSKSortFilter(tableInfo))
|
||||||
|
|
||||||
assert.Equal(t, items[0], testNumberData[2])
|
assert.Equal(t, items[0], testNumberData[2])
|
||||||
assert.Equal(t, items[1], testNumberData[1])
|
assert.Equal(t, items[1], testNumberData[1])
|
||||||
|
@ -41,7 +41,7 @@ func TestSort(t *testing.T) {
|
||||||
items := make([]models.Item, len(testBoolData))
|
items := make([]models.Item, len(testBoolData))
|
||||||
copy(items, testBoolData)
|
copy(items, testBoolData)
|
||||||
|
|
||||||
models.Sort(items, tableInfo)
|
models.Sort(items, models.PKSKSortFilter(tableInfo))
|
||||||
|
|
||||||
assert.Equal(t, items[0], testBoolData[2])
|
assert.Equal(t, items[0], testBoolData[2])
|
||||||
assert.Equal(t, items[1], testBoolData[1])
|
assert.Equal(t, items[1], testBoolData[1])
|
||||||
|
|
|
@ -86,8 +86,6 @@ func (s *Service) doScan(
|
||||||
}, errors.Wrapf(err, "unable to scan table %v", tableInfo.Name)
|
}, errors.Wrapf(err, "unable to scan table %v", tableInfo.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
models.Sort(results, tableInfo)
|
|
||||||
|
|
||||||
resultSet := &models.ResultSet{
|
resultSet := &models.ResultSet{
|
||||||
TableInfo: tableInfo,
|
TableInfo: tableInfo,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
|
@ -97,6 +95,7 @@ func (s *Service) doScan(
|
||||||
}
|
}
|
||||||
resultSet.SetItems(results)
|
resultSet.SetItems(results)
|
||||||
resultSet.RefreshColumns()
|
resultSet.RefreshColumns()
|
||||||
|
resultSet.Sort(models.PKSKSortFilter(tableInfo))
|
||||||
|
|
||||||
return resultSet, err
|
return resultSet, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ func Default() *KeyBindings {
|
||||||
ResetColumns: key.NewBinding(key.WithKeys("R", "reset columns")),
|
ResetColumns: key.NewBinding(key.WithKeys("R", "reset columns")),
|
||||||
AddColumn: key.NewBinding(key.WithKeys("a", "add new column")),
|
AddColumn: key.NewBinding(key.WithKeys("a", "add new column")),
|
||||||
DeleteColumn: key.NewBinding(key.WithKeys("d", "delete column")),
|
DeleteColumn: key.NewBinding(key.WithKeys("d", "delete column")),
|
||||||
|
SortByColumn: key.NewBinding(key.WithKeys("s", "sort by column")),
|
||||||
},
|
},
|
||||||
TableView: &TableKeyBinding{
|
TableView: &TableKeyBinding{
|
||||||
MoveUp: key.NewBinding(key.WithKeys("i", "up")),
|
MoveUp: key.NewBinding(key.WithKeys("i", "up")),
|
||||||
|
|
|
@ -16,6 +16,7 @@ type FieldsPopupBinding struct {
|
||||||
ResetColumns key.Binding `keymap:"reset-columns"`
|
ResetColumns key.Binding `keymap:"reset-columns"`
|
||||||
AddColumn key.Binding `keymap:"add-column"`
|
AddColumn key.Binding `keymap:"add-column"`
|
||||||
DeleteColumn key.Binding `keymap:"delete-column"`
|
DeleteColumn key.Binding `keymap:"delete-column"`
|
||||||
|
SortByColumn key.Binding `keymap:"sort-by-column"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TableKeyBinding struct {
|
type TableKeyBinding struct {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/columns"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/columns"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/layout"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/teamodels/layout"
|
||||||
|
@ -25,8 +26,9 @@ type colListModel struct {
|
||||||
keyBinding *keybindings.KeyBindings
|
keyBinding *keybindings.KeyBindings
|
||||||
colController *controllers.ColumnsController
|
colController *controllers.ColumnsController
|
||||||
|
|
||||||
rows []table.Row
|
rows []table.Row
|
||||||
table table.Model
|
table table.Model
|
||||||
|
sortCriteria models.SortCriteria
|
||||||
}
|
}
|
||||||
|
|
||||||
func newColListModel(keyBinding *keybindings.KeyBindings, colController *controllers.ColumnsController) *colListModel {
|
func newColListModel(keyBinding *keybindings.KeyBindings, colController *controllers.ColumnsController) *colListModel {
|
||||||
|
@ -68,6 +70,8 @@ func (m *colListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, events.SetTeaMessage(m.colController.AddColumn(m.table.Cursor()))
|
return m, events.SetTeaMessage(m.colController.AddColumn(m.table.Cursor()))
|
||||||
case key.Matches(msg, m.keyBinding.ColumnPopup.DeleteColumn):
|
case key.Matches(msg, m.keyBinding.ColumnPopup.DeleteColumn):
|
||||||
return m, events.SetTeaMessage(m.colController.DeleteColumn(m.table.Cursor()))
|
return m, events.SetTeaMessage(m.colController.DeleteColumn(m.table.Cursor()))
|
||||||
|
case key.Matches(msg, m.keyBinding.ColumnPopup.SortByColumn):
|
||||||
|
return m, events.SetTeaMessage(m.colController.SortByColumn(m.table.Cursor()))
|
||||||
|
|
||||||
// Main table nav
|
// Main table nav
|
||||||
case key.Matches(msg, m.keyBinding.TableView.ColLeft):
|
case key.Matches(msg, m.keyBinding.TableView.ColLeft):
|
||||||
|
@ -122,6 +126,7 @@ func (c *colListModel) Resize(w, h int) layout.ResizingModel {
|
||||||
|
|
||||||
func (c *colListModel) refreshTable() {
|
func (c *colListModel) refreshTable() {
|
||||||
colsFromController := c.colController.Columns()
|
colsFromController := c.colController.Columns()
|
||||||
|
c.sortCriteria = c.colController.SortCriteria()
|
||||||
if len(c.rows) != len(colsFromController.Columns) {
|
if len(c.rows) != len(colsFromController.Columns) {
|
||||||
c.setColumnsFromModel(colsFromController)
|
c.setColumnsFromModel(colsFromController)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func (m *Model) 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 controllers.ShowColumnOverlay:
|
case controllers.ShowColumnOverlay:
|
||||||
|
m.colListModel.sortCriteria = m.columnsController.SortCriteria()
|
||||||
m.colListModel.setColumnsFromModel(m.columnsController.Columns())
|
m.colListModel.setColumnsFromModel(m.columnsController.Columns())
|
||||||
m.compositor.SetOverlay(m.colListModel, m.w/2-overlayWidth/2, m.h/2-overlayHeight/2, overlayWidth, overlayHeight)
|
m.compositor.SetOverlay(m.colListModel, m.w/2-overlayWidth/2, m.h/2-overlayHeight/2, overlayWidth, overlayHeight)
|
||||||
case controllers.HideColumnOverlay:
|
case controllers.HideColumnOverlay:
|
||||||
|
|
|
@ -3,6 +3,7 @@ package colselector
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/evaluators"
|
||||||
table "github.com/lmika/go-bubble-table"
|
table "github.com/lmika/go-bubble-table"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
@ -23,9 +24,17 @@ func (clr colListRowModel) Render(w io.Writer, model table.Model, index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
col := clr.m.colController.Columns().Columns[index]
|
col := clr.m.colController.Columns().Columns[index]
|
||||||
if !col.Hidden {
|
ff := clr.m.sortCriteria.FirstField()
|
||||||
fmt.Fprintln(w, style.Render(fmt.Sprintf("⋅\t%v", col.Name)))
|
switch {
|
||||||
} else {
|
case col.Hidden:
|
||||||
fmt.Fprintln(w, style.Render(fmt.Sprintf("✕\t%v", col.Name)))
|
fmt.Fprintln(w, style.Render(fmt.Sprintf("✕\t%v", col.Name)))
|
||||||
|
case evaluators.Equals(ff.Field, col.Evaluator):
|
||||||
|
if ff.Asc {
|
||||||
|
fmt.Fprintln(w, style.Render(fmt.Sprintf("v\t%v", col.Name)))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(w, style.Render(fmt.Sprintf("^\t%v", col.Name)))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Fprintln(w, style.Render(fmt.Sprintf("⋅\t%v", col.Name)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,9 @@ func (m *listModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
if onSel := m.event.OnSelected; onSel != nil {
|
if onSel := m.event.OnSelected; onSel != nil {
|
||||||
cc.Add(events.SetTeaMessage(onSel(m.event.Items[m.list.Index()])))
|
cc.Add(events.SetTeaMessage(onSel(m.event.Items[m.list.Index()])))
|
||||||
}
|
}
|
||||||
return m, events.SetTeaMessage(controllers.HideColumnOverlay{})
|
return m, events.SetTeaMessage(controllers.HideRelatedItemsOverlay{})
|
||||||
case key.Matches(msg, keyEsc):
|
case key.Matches(msg, keyEsc):
|
||||||
return m, events.SetTeaMessage(controllers.HideColumnOverlay{})
|
return m, events.SetTeaMessage(controllers.HideRelatedItemsOverlay{})
|
||||||
default:
|
default:
|
||||||
m.list = cc.Collect(m.list.Update(msg)).(list.Model)
|
m.list = cc.Collect(m.list.Update(msg)).(list.Model)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
|
||||||
m.listModel.setItems(msg, newHeight)
|
m.listModel.setItems(msg, newHeight)
|
||||||
m.compositor.SetOverlay(m.listModel, m.w/2-overlayWidth/2, m.h/2-newHeight/2, overlayWidth, newHeight)
|
m.compositor.SetOverlay(m.listModel, m.w/2-overlayWidth/2, m.h/2-newHeight/2, overlayWidth, newHeight)
|
||||||
case controllers.HideColumnOverlay:
|
case controllers.HideRelatedItemsOverlay:
|
||||||
m.compositor.ClearOverlay()
|
m.compositor.ClearOverlay()
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
m.compositor = cc.Collect(m.compositor.Update(msg)).(*layout.Compositor)
|
m.compositor = cc.Collect(m.compositor.Update(msg)).(*layout.Compositor)
|
||||||
|
|
Loading…
Reference in a new issue