Repository: Evertras/bubble-table
Pull request #62 (2022-04-14 11:58:07)

Add WithColumns option
Helps #59 while providing the ability to resize, add, and remove columns too.


Patch:
diff --git a/examples/updates/main.go b/examples/updates/main.go
index 1f0eea1..207a9ac 100644
--- a/examples/updates/main.go
+++ b/examples/updates/main.go
@@ -32,14 +32,8 @@ type Model struct {
 }
 
 func NewModel() Model {
-	columns := []table.Column{
-		table.NewColumn(columnKeyID, "ID", 10),
-		table.NewColumn(columnKeyScore, "Score", 8),
-		table.NewColumn(columnKeyStatus, "Status", 10),
-	}
-
 	return Model{
-		table:       table.New(columns),
+		table:       table.New(generateColumns(0)),
 		updateDelay: time.Second,
 	}
 }
@@ -58,6 +52,25 @@ func refreshDataCmd() tea.Msg {
 	}
 }
 
+// Generate columns based on how many are critical to show some summary
+func generateColumns(numCritical int) []table.Column {
+	// Show how many critical there are
+	statusStr := fmt.Sprintf("Score (%d)", numCritical)
+	statusCol := table.NewColumn(columnKeyStatus, statusStr, 10)
+
+	if numCritical > 3 {
+		// This normally applies the critical style to everything in the column,
+		// but in this case we apply a row style which overrides it anyway.
+		statusCol = statusCol.WithStyle(styleCritical)
+	}
+
+	return []table.Column{
+		table.NewColumn(columnKeyID, "ID", 10),
+		table.NewColumn(columnKeyScore, "Score", 8),
+		statusCol,
+	}
+}
+
 func (m Model) Init() tea.Cmd {
 	return refreshDataCmd
 }
@@ -91,8 +104,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 	case []*SomeData:
 		m.data = msg
 
-		// Reapply the new data
-		m.table = m.table.WithRows(generateRowsFromData(m.data))
+		numCritical := 0
+
+		for _, d := range msg {
+			if d.Status == "Critical" {
+				numCritical++
+			}
+		}
+
+		// Reapply the new data and the new columns based on critical count
+		m.table = m.table.WithRows(generateRowsFromData(m.data)).WithColumns(generateColumns(numCritical))
 
 		// This can be from any source, but for demo purposes let's party!
 		delay := m.updateDelay
diff --git a/table/options.go b/table/options.go
index 439c40f..d2d596a 100644
--- a/table/options.go
+++ b/table/options.go
@@ -230,3 +230,15 @@ func (m Model) WithCurrentPage(currentPage int) Model {
 
 	return m
 }
+
+// WithColumns sets the visible columns for the table, so that columns can be
+// added/removed/resized or headers rewritten.
+func (m Model) WithColumns(columns []Column) Model {
+	// Deep copy to avoid edits
+	m.columns = make([]Column, len(columns))
+	copy(m.columns, columns)
+
+	m.recalculateWidth()
+
+	return m
+}


Test patch:
diff --git a/table/view_test.go b/table/view_test.go
index ce99a0b..9868f21 100644
--- a/table/view_test.go
+++ b/table/view_test.go
@@ -562,3 +562,40 @@ func TestSimpleFlex3x3AtAllTargetWidths(t *testing.T) {
 		}
 	}
 }
+
+func TestViewResizesWhenColumnsChange(t *testing.T) {
+	model := New([]Column{
+		NewColumn("id", "ID", 4),
+	}).WithRows([]Row{
+		NewRow(RowData{"id": "1", "score": 3}),
+		NewRow(RowData{"id": "2", "score": 4}),
+	})
+
+	const expectedTableOriginal = `┏━━━━┓
+┃  ID┃
+┣━━━━┫
+┃   1┃
+┃   2┃
+┗━━━━┛`
+
+	// Lowercased, resized, and new column added
+	const expectedTableUpdated = `┏━━━━━┳━━━━━━┓
+┃   id┃ Score┃
+┣━━━━━╋━━━━━━┫
+┃    1┃     3┃
+┃    2┃     4┃
+┗━━━━━┻━━━━━━┛`
+
+	rendered := model.View()
+
+	assert.Equal(t, expectedTableOriginal, rendered)
+
+	model = model.WithColumns([]Column{
+		NewColumn("id", "id", 5),
+		NewColumn("score", "Score", 6),
+	})
+
+	rendered = model.View()
+
+	assert.Equal(t, expectedTableUpdated, rendered)
+}