feat(tui): responsive table columns — expand headers with terminal width
Replace hardcoded column widths with dynamic layout system: - Each column has short/full header and min/max width - At narrow terminals: LAT, UP%, RT, compact widths - At wide terminals: LATENCY, UPTIME, RETRIES, expanded widths - Surplus space distributed left-to-right across expandable columns - Headers switch between short/full based on actual column width Column definitions: # (4-6) TYPE (8-10) STATUS (8-10) LAT/LATENCY (5-10) UP%/UPTIME (5-8) SSL (5-7) RT/RETRIES (5-9)
This commit is contained in:
+92
-13
@@ -334,15 +334,50 @@ func fmtDuration(d time.Duration) string {
|
|||||||
return fmt.Sprintf("%dd", days)
|
return fmt.Sprintf("%dd", days)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) dynamicWidths() (nameW, sparkW int) {
|
type tableLayout struct {
|
||||||
fixed := 6 + 10 + 10 + 8 + 8 + 7 + 9 // #, TYPE, STATUS, LATENCY, UPTIME, SSL, RETRY
|
nameW, sparkW int
|
||||||
overhead := 30 // cell padding + borders
|
headers []string
|
||||||
avail := m.termWidth - chromePadH - 2 - fixed - overhead
|
colWidths []int
|
||||||
if avail < 30 {
|
}
|
||||||
avail = 30
|
|
||||||
|
func (m Model) computeLayout() tableLayout {
|
||||||
|
type colDef struct {
|
||||||
|
short string
|
||||||
|
full string
|
||||||
|
minWidth int
|
||||||
|
maxWidth int
|
||||||
}
|
}
|
||||||
nameW = avail / 2
|
|
||||||
sparkW = avail - nameW - 2 // -2 for spark column padding
|
cols := []colDef{
|
||||||
|
{"#", "#", 4, 6},
|
||||||
|
{"", "", 0, 0}, // NAME (dynamic)
|
||||||
|
{"TYPE", "TYPE", 8, 10},
|
||||||
|
{"STATUS", "STATUS", 8, 10},
|
||||||
|
{"LAT", "LATENCY", 5, 10},
|
||||||
|
{"UP%", "UPTIME", 5, 8},
|
||||||
|
{"", "", 0, 0}, // HISTORY (dynamic)
|
||||||
|
{"SSL", "SSL", 5, 7},
|
||||||
|
{"RT", "RETRIES", 5, 9},
|
||||||
|
}
|
||||||
|
|
||||||
|
overhead := 30
|
||||||
|
usable := m.termWidth - chromePadH - 2 - overhead
|
||||||
|
if usable < 80 {
|
||||||
|
usable = 80
|
||||||
|
}
|
||||||
|
|
||||||
|
fixedMin := 0
|
||||||
|
for i, c := range cols {
|
||||||
|
if i == 1 || i == 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fixedMin += c.minWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
avail := usable - fixedMin
|
||||||
|
nameW := avail / 2
|
||||||
|
sparkW := avail - nameW - 2
|
||||||
|
|
||||||
if nameW < 13 {
|
if nameW < 13 {
|
||||||
nameW = 13
|
nameW = 13
|
||||||
}
|
}
|
||||||
@@ -355,7 +390,50 @@ func (m Model) dynamicWidths() (nameW, sparkW int) {
|
|||||||
if sparkW > 60 {
|
if sparkW > 60 {
|
||||||
sparkW = 60
|
sparkW = 60
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
surplus := usable - fixedMin - nameW - sparkW - 2
|
||||||
|
if surplus < 0 {
|
||||||
|
surplus = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := make([]string, len(cols))
|
||||||
|
widths := make([]int, len(cols))
|
||||||
|
for i, c := range cols {
|
||||||
|
if i == 1 {
|
||||||
|
headers[i] = "NAME"
|
||||||
|
widths[i] = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i == 6 {
|
||||||
|
headers[i] = "HISTORY"
|
||||||
|
widths[i] = sparkW + 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
w := c.minWidth
|
||||||
|
expand := c.maxWidth - c.minWidth
|
||||||
|
if surplus >= expand {
|
||||||
|
w = c.maxWidth
|
||||||
|
surplus -= expand
|
||||||
|
} else if surplus > 0 {
|
||||||
|
w += surplus
|
||||||
|
surplus = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if w >= len(c.full)+2 {
|
||||||
|
headers[i] = c.full
|
||||||
|
} else {
|
||||||
|
headers[i] = c.short
|
||||||
|
}
|
||||||
|
widths[i] = w
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableLayout{
|
||||||
|
nameW: nameW,
|
||||||
|
sparkW: sparkW,
|
||||||
|
headers: headers,
|
||||||
|
colWidths: widths,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) viewSitesTab() string {
|
func (m Model) viewSitesTab() string {
|
||||||
@@ -373,12 +451,13 @@ func (m Model) viewSitesTab() string {
|
|||||||
return "\n" + welcome
|
return "\n" + welcome
|
||||||
}
|
}
|
||||||
|
|
||||||
nameW, sparkWidth := m.dynamicWidths()
|
layout := m.computeLayout()
|
||||||
colWidths := []int{6, 0, 10, 10, 8, 8, sparkWidth + 2, 7, 9}
|
nameW := layout.nameW
|
||||||
|
sparkWidth := layout.sparkW
|
||||||
|
|
||||||
var groupRows map[int]bool
|
var groupRows map[int]bool
|
||||||
return m.renderTable(
|
return m.renderTable(
|
||||||
[]string{"#", "NAME", "TYPE", "STATUS", "LAT", "UPTIME", "HISTORY", "SSL", "RETRY"},
|
layout.headers,
|
||||||
len(m.sites),
|
len(m.sites),
|
||||||
func(start, end int) [][]string {
|
func(start, end int) [][]string {
|
||||||
groupRows = make(map[int]bool)
|
groupRows = make(map[int]bool)
|
||||||
@@ -444,7 +523,7 @@ func (m Model) viewSitesTab() string {
|
|||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
},
|
},
|
||||||
colWidths,
|
layout.colWidths,
|
||||||
func(row, col int) *lipgloss.Style {
|
func(row, col int) *lipgloss.Style {
|
||||||
if groupRows[row] {
|
if groupRows[row] {
|
||||||
s := siteGroupStyle
|
s := siteGroupStyle
|
||||||
|
|||||||
Reference in New Issue
Block a user