kops/vendor/github.com/evertras/bubble-table/table/row.go

199 lines
4.9 KiB
Go

package table
import (
"fmt"
"github.com/charmbracelet/lipgloss"
)
// RowData is a map of string column keys to interface{} data. Data with a key
// that matches a column key will be displayed. Data with a key that does not
// match a column key will not be displayed, but will remain attached to the Row.
// This can be useful for attaching hidden metadata for future reference when
// retrieving rows.
type RowData map[string]interface{}
// Row represents a row in the table with some data keyed to the table columns>
// Can have a style applied to it such as color/bold. Create using NewRow().
type Row struct {
Style lipgloss.Style
Data RowData
selected bool
}
// NewRow creates a new row and copies the given row data.
func NewRow(data RowData) Row {
row := Row{
Data: make(map[string]interface{}),
}
for key, val := range data {
// Doesn't deep copy val, but close enough for now...
row.Data[key] = val
}
return row
}
// WithStyle uses the given style for the text in the row.
func (r Row) WithStyle(style lipgloss.Style) Row {
r.Style = style.Copy()
return r
}
//nolint:nestif // This has many ifs, but they're short
func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Style, borderStyle lipgloss.Style) string {
cellStyle := rowStyle.Copy().Inherit(column.style).Inherit(m.baseStyle)
var str string
if column.key == columnKeySelect {
if row.selected {
str = m.selectedText
} else {
str = m.unselectedText
}
} else if column.key == columnKeyOverflowRight {
cellStyle = cellStyle.Align(lipgloss.Right)
str = ">"
} else if column.key == columnKeyOverflowLeft {
str = "<"
} else {
var data interface{}
if entry, exists := row.Data[column.key]; exists {
data = entry
} else if m.missingDataIndicator != nil {
data = m.missingDataIndicator
} else {
data = ""
}
fmtString := "%v"
if column.fmtString != "" {
fmtString = column.fmtString
}
switch entry := data.(type) {
case StyledCell:
str = fmt.Sprintf(fmtString, entry.Data)
cellStyle = entry.Style.Copy().Inherit(cellStyle)
default:
str = fmt.Sprintf(fmtString, entry)
}
}
cellStyle = cellStyle.Inherit(borderStyle)
cellStr := cellStyle.Render(limitStr(str, column.width))
return cellStr
}
// This is long and could use some refactoring in the future, but not quite sure
// how to pick it apart yet.
//
//nolint:funlen, cyclop, gocognit
func (m Model) renderRow(rowIndex int, last bool) string {
numColumns := len(m.columns)
row := m.GetVisibleRows()[rowIndex]
highlighted := rowIndex == m.rowCursorIndex
totalRenderedWidth := 0
columnStrings := []string{}
rowStyle := row.Style.Copy()
if m.focused && highlighted {
rowStyle = rowStyle.Inherit(m.highlightStyle)
}
stylesInner, stylesLast := m.styleRows()
for columnIndex, column := range m.columns {
var borderStyle lipgloss.Style
var rowStyles borderStyleRow
if !last {
rowStyles = stylesInner
} else {
rowStyles = stylesLast
}
if m.horizontalScrollOffsetCol > 0 && columnIndex == m.horizontalScrollFreezeColumnsCount {
var borderStyle lipgloss.Style
if columnIndex == 0 {
borderStyle = rowStyles.left.Copy()
} else {
borderStyle = rowStyles.inner.Copy()
}
rendered := m.renderRowColumnData(row, genOverflowColumnLeft(1), rowStyle, borderStyle)
totalRenderedWidth += lipgloss.Width(rendered)
columnStrings = append(columnStrings, rendered)
}
if columnIndex >= m.horizontalScrollFreezeColumnsCount &&
columnIndex < m.horizontalScrollOffsetCol+m.horizontalScrollFreezeColumnsCount {
continue
}
if len(columnStrings) == 0 {
borderStyle = rowStyles.left
} else if columnIndex < numColumns-1 {
borderStyle = rowStyles.inner
} else {
borderStyle = rowStyles.right
}
cellStr := m.renderRowColumnData(row, column, rowStyle, borderStyle)
if m.maxTotalWidth != 0 {
renderedWidth := lipgloss.Width(cellStr)
const (
borderAdjustment = 1
overflowColWidth = 2
)
targetWidth := m.maxTotalWidth - overflowColWidth
if columnIndex == len(m.columns)-1 {
// If this is the last header, we don't need to account for the
// overflow arrow column
targetWidth = m.maxTotalWidth
}
if totalRenderedWidth+renderedWidth > targetWidth {
overflowWidth := m.maxTotalWidth - totalRenderedWidth - borderAdjustment
overflowStyle := genOverflowStyle(rowStyles.right, overflowWidth)
overflowColumn := genOverflowColumnRight(overflowWidth)
overflowStr := m.renderRowColumnData(row, overflowColumn, rowStyle, overflowStyle)
columnStrings = append(columnStrings, overflowStr)
break
}
totalRenderedWidth += renderedWidth
}
columnStrings = append(columnStrings, cellStr)
}
return lipgloss.JoinHorizontal(lipgloss.Bottom, columnStrings...)
}
// Selected returns a copy of the row that's set to be selected or deselected.
// The old row is not changed in-place.
func (r Row) Selected(selected bool) Row {
r.selected = selected
return r
}