mirror of
https://github.com/taigrr/teaqlite.git
synced 2026-04-02 04:59:03 -07:00
174 lines
4.4 KiB
Go
174 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
// Row Detail Model
|
|
type rowDetailModel struct {
|
|
shared *sharedData
|
|
rowIndex int
|
|
selectedCol int
|
|
fromQuery bool // true if came from query results
|
|
}
|
|
|
|
func newRowDetailModel(shared *sharedData, rowIndex int) *rowDetailModel {
|
|
return &rowDetailModel{
|
|
shared: shared,
|
|
rowIndex: rowIndex,
|
|
selectedCol: 0,
|
|
fromQuery: false, // default to false, will be set by caller if needed
|
|
}
|
|
}
|
|
|
|
func (m *rowDetailModel) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (m *rowDetailModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
return m.handleNavigation(msg)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *rowDetailModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|
switch msg.String() {
|
|
case "esc":
|
|
if m.fromQuery {
|
|
return m, func() tea.Msg { return returnToQueryMsg{} }
|
|
} else {
|
|
return m, func() tea.Msg {
|
|
return switchToTableDataMsg{tableIndex: m.shared.selectedTable}
|
|
}
|
|
}
|
|
|
|
case "enter":
|
|
if len(m.shared.filteredData) > 0 && m.rowIndex < len(m.shared.filteredData) &&
|
|
m.selectedCol < len(m.shared.columns) {
|
|
return m, func() tea.Msg {
|
|
return switchToEditCellMsg{rowIndex: m.rowIndex, colIndex: m.selectedCol}
|
|
}
|
|
}
|
|
|
|
case "up", "k":
|
|
if m.selectedCol > 0 {
|
|
m.selectedCol--
|
|
}
|
|
|
|
case "down", "j":
|
|
if m.selectedCol < len(m.shared.columns)-1 {
|
|
m.selectedCol++
|
|
}
|
|
|
|
case "r":
|
|
return m, func() tea.Msg { return refreshDataMsg{} }
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m *rowDetailModel) getVisibleRowCount() int {
|
|
reservedLines := 9
|
|
return max(1, m.shared.height-reservedLines)
|
|
}
|
|
|
|
func (m *rowDetailModel) View() string {
|
|
var content strings.Builder
|
|
|
|
if m.fromQuery {
|
|
content.WriteString(titleStyle.Render("Row Detail: Query Result"))
|
|
} else {
|
|
tableName := m.shared.filteredTables[m.shared.selectedTable]
|
|
content.WriteString(titleStyle.Render(fmt.Sprintf("Row Detail: %s", tableName)))
|
|
}
|
|
content.WriteString("\n\n")
|
|
|
|
if m.rowIndex >= len(m.shared.filteredData) {
|
|
content.WriteString("Invalid row selection")
|
|
} else {
|
|
row := m.shared.filteredData[m.rowIndex]
|
|
|
|
// Show as 2-column table: Column | Value
|
|
colWidth := max(15, m.shared.width/3)
|
|
valueWidth := max(20, m.shared.width-colWidth-5)
|
|
|
|
// Header
|
|
headerRow := fmt.Sprintf("%-*s | %-*s", colWidth, "Column", valueWidth, "Value")
|
|
content.WriteString(selectedStyle.Render(headerRow))
|
|
content.WriteString("\n")
|
|
|
|
// Separator
|
|
separator := strings.Repeat("-", colWidth) + "-+-" + strings.Repeat("-", valueWidth)
|
|
content.WriteString(separator)
|
|
content.WriteString("\n")
|
|
|
|
// Data rows
|
|
visibleRows := m.getVisibleRowCount()
|
|
displayRows := min(len(m.shared.columns), visibleRows)
|
|
|
|
for i := range displayRows {
|
|
if i >= len(m.shared.columns) || i >= len(row) {
|
|
break
|
|
}
|
|
|
|
col := m.shared.columns[i]
|
|
val := row[i]
|
|
|
|
// For long values, show them wrapped on multiple lines
|
|
if len(val) > valueWidth {
|
|
// First line with column name
|
|
firstLine := fmt.Sprintf("%-*s | %-*s",
|
|
colWidth, truncateString(col, colWidth),
|
|
valueWidth, truncateString(val, valueWidth))
|
|
|
|
if i == m.selectedCol {
|
|
content.WriteString(selectedStyle.Render(firstLine))
|
|
} else {
|
|
content.WriteString(normalStyle.Render(firstLine))
|
|
}
|
|
content.WriteString("\n")
|
|
|
|
// Additional lines for wrapped text (if there's space)
|
|
if len(val) > valueWidth && visibleRows > displayRows {
|
|
wrappedLines := wrapText(val, valueWidth)
|
|
for j, wrappedLine := range wrappedLines[1:] { // Skip first line already shown
|
|
if j >= 2 { // Limit to 3 total lines per field
|
|
break
|
|
}
|
|
continuationLine := fmt.Sprintf("%-*s | %-*s",
|
|
colWidth, "", valueWidth, wrappedLine)
|
|
if i == m.selectedCol {
|
|
content.WriteString(selectedStyle.Render(continuationLine))
|
|
} else {
|
|
content.WriteString(normalStyle.Render(continuationLine))
|
|
}
|
|
content.WriteString("\n")
|
|
}
|
|
}
|
|
} else {
|
|
// Normal single line
|
|
dataRow := fmt.Sprintf("%-*s | %-*s",
|
|
colWidth, truncateString(col, colWidth),
|
|
valueWidth, val)
|
|
|
|
if i == m.selectedCol {
|
|
content.WriteString(selectedStyle.Render(dataRow))
|
|
} else {
|
|
content.WriteString(normalStyle.Render(dataRow))
|
|
}
|
|
content.WriteString("\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
content.WriteString("\n")
|
|
content.WriteString(helpStyle.Render("↑/↓: select field • enter: edit • esc: back • q: quit"))
|
|
|
|
return content.String()
|
|
}
|
|
|