Files
teaqlite/internal/app/edit_cell.go
Tai Groot aa4c97c553 feat(tests): add comprehensive test suite, update deps and Go 1.26.1
- Add 20+ tests for utility functions, SharedData, and Model
- Tests cover: LoadTables, LoadTableData, UpdateCell, pagination,
  table inference, focus/blur, empty database, invalid indices
- Update Go to 1.26.1, upgrade all dependencies
- Replace custom Min/Max with Go builtin min/max
- Format all files with goimports
- Add staticcheck to CI workflow
2026-03-13 02:46:03 +00:00

163 lines
3.2 KiB
Go

package app
import (
"fmt"
"time"
"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)
type EditCellModel struct {
Shared *SharedData
rowIndex int
colIndex int
input textinput.Model
blinkState bool
keyMap EditCellKeyMap
help help.Model
showFullHelp bool
focused bool
id int
}
// EditCellOption is a functional option for configuring EditCellModel
type EditCellOption func(*EditCellModel)
// WithEditCellKeyMap sets the key map
func WithEditCellKeyMap(km EditCellKeyMap) EditCellOption {
return func(m *EditCellModel) {
m.keyMap = km
}
}
func blinkCmd() tea.Cmd {
return tea.Tick(time.Millisecond*500, func(t time.Time) tea.Msg {
return blinkMsg{}
})
}
func NewEditCellModel(shared *SharedData, rowIndex, colIndex int, opts ...EditCellOption) *EditCellModel {
value := ""
if rowIndex < len(shared.FilteredData) && colIndex < len(shared.FilteredData[rowIndex]) {
value = shared.FilteredData[rowIndex][colIndex]
}
input := textinput.New()
input.SetValue(value)
input.Width = 50
input.Focus()
m := &EditCellModel{
Shared: shared,
rowIndex: rowIndex,
colIndex: colIndex,
input: input,
blinkState: true,
keyMap: DefaultEditCellKeyMap(),
help: help.New(),
focused: true,
id: nextID(),
}
// Apply options
for _, opt := range opts {
opt(m)
}
return m
}
// ID returns the unique ID of the model
func (m EditCellModel) ID() int {
return m.id
}
// Focus sets the focus state
func (m *EditCellModel) Focus() {
m.focused = true
m.input.Focus()
}
// Blur removes focus
func (m *EditCellModel) Blur() {
m.focused = false
m.input.Blur()
}
// Focused returns the focus state
func (m EditCellModel) Focused() bool {
return m.focused
}
func (m *EditCellModel) Init() tea.Cmd {
return tea.Batch(
textinput.Blink,
blinkCmd(),
)
}
func (m *EditCellModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if !m.focused {
return m, nil
}
var cmds []tea.Cmd
switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case blinkMsg:
m.blinkState = !m.blinkState
cmds = append(cmds, blinkCmd())
case tea.KeyMsg:
switch {
case key.Matches(msg, m.keyMap.Save):
return m, func() tea.Msg {
return UpdateCellMsg{
RowIndex: m.rowIndex,
ColIndex: m.colIndex,
Value: m.input.Value(),
}
}
case key.Matches(msg, m.keyMap.Cancel):
return m, func() tea.Msg {
return SwitchToRowDetailMsg{RowIndex: m.rowIndex}
}
}
}
// Update the input for all other messages
var cmd tea.Cmd
m.input, cmd = m.input.Update(msg)
if cmd != nil {
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
func (m *EditCellModel) View() string {
columnName := ""
if m.colIndex < len(m.Shared.Columns) {
columnName = m.Shared.Columns[m.colIndex]
}
content := fmt.Sprintf("%s\n\n", TitleStyle.Render(fmt.Sprintf("Edit Cell: %s", columnName)))
content += fmt.Sprintf("Value: %s\n\n", m.input.View())
if m.showFullHelp {
content += m.help.FullHelpView(m.keyMap.FullHelp())
} else {
content += m.help.ShortHelpView(m.keyMap.ShortHelp())
}
return content
}