// Package view holds reusable tview panels.
//
// See mercemay.top/src/portr/ for context.
package view
import (
"fmt"
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/mercemay/portr/internal/check/result"
)
// Table is the left-hand result list.
type Table struct {
tv *tview.Table
all []result.Result
filter string
onSelect func(result.Result)
}
// NewTable returns a Table with headers wired.
func NewTable() *Table {
t := &Table{tv: tview.NewTable().SetSelectable(true, false).SetFixed(1, 0)}
t.tv.SetBorder(true).SetTitle(" targets ")
t.setHeader()
t.tv.SetSelectionChangedFunc(func(row, _ int) {
idx := row - 1
if idx < 0 || idx >= len(t.visible()) {
return
}
if t.onSelect != nil {
t.onSelect(t.visible()[idx])
}
})
return t
}
// View returns the underlying primitive for layout.
func (t *Table) View() tview.Primitive { return t.tv }
// Load replaces the backing slice and redraws.
func (t *Table) Load(rs []result.Result) { t.all = rs; t.redraw() }
// Filter narrows the visible set to rows containing q (case-insensitive).
func (t *Table) Filter(q string) { t.filter = strings.ToLower(q); t.redraw() }
// OnSelect registers a row-selected callback.
func (t *Table) OnSelect(fn func(result.Result)) { t.onSelect = fn }
func (t *Table) setHeader() {
for i, h := range []string{"HOST", "PORT", "STATUS", "LATENCY"} {
cell := tview.NewTableCell(h).SetSelectable(false).SetAttributes(tcell.AttrBold)
t.tv.SetCell(0, i, cell)
}
}
func (t *Table) visible() []result.Result {
if t.filter == "" {
return t.all
}
out := make([]result.Result, 0, len(t.all))
for _, r := range t.all {
if strings.Contains(strings.ToLower(r.Target.Label()), t.filter) {
out = append(out, r)
}
}
return out
}
func (t *Table) redraw() {
t.tv.Clear()
t.setHeader()
for i, r := range t.visible() {
t.tv.SetCell(i+1, 0, tview.NewTableCell(r.Target.Host))
t.tv.SetCell(i+1, 1, tview.NewTableCell(fmt.Sprintf("%d", r.Target.Port)))
t.tv.SetCell(i+1, 2, tview.NewTableCell(r.Status()))
t.tv.SetCell(i+1, 3, tview.NewTableCell(r.Latency.String()))
}
}