Repository: Evertras/bubble-table
Pull request #113 (2022-06-24 12:54:52)

Add option to toggle whether pagination wraps
Helps #112 

Adds option to set whether pagination wraps or not.  Defaults to true to keep current behavior.


Patch:
diff --git a/table/model.go b/table/model.go
index b8d5c93..46709b9 100644
--- a/table/model.go
+++ b/table/model.go
@@ -48,8 +48,9 @@ type Model struct {
 	staticFooter  string
 
 	// Pagination
-	pageSize    int
-	currentPage int
+	pageSize           int
+	currentPage        int
+	paginationWrapping bool
 
 	// Sorting, where a stable sort is applied from first element to last so
 	// that elements are grouped by the later elements.
@@ -96,6 +97,8 @@ func New(columns []Column) Model {
 
 		filterTextInput: filterInput,
 		baseStyle:       lipgloss.NewStyle().Align(lipgloss.Right),
+
+		paginationWrapping: true,
 	}
 
 	// Do a full deep copy to avoid unexpected edits
diff --git a/table/options.go b/table/options.go
index e58c399..6b08c8c 100644
--- a/table/options.go
+++ b/table/options.go
@@ -161,13 +161,21 @@ func (m Model) WithPageSize(pageSize int) Model {
 	return m
 }
 
-// WithNoPagination disable pagination in the table.
+// WithNoPagination disables pagination in the table.
 func (m Model) WithNoPagination() Model {
 	m.pageSize = 0
 
 	return m
 }
 
+// WithPaginationWrapping sets whether to wrap around from the beginning to the
+// end when navigating through pages.  Defaults to true.
+func (m Model) WithPaginationWrapping(wrapping bool) Model {
+	m.paginationWrapping = wrapping
+
+	return m
+}
+
 // WithSelectedText describes what text to show when selectable rows are enabled.
 // The selectable column header will use the selected text string.
 func (m Model) WithSelectedText(unselected, selected string) Model {
diff --git a/table/pagination.go b/table/pagination.go
index 4780156..9300343 100644
--- a/table/pagination.go
+++ b/table/pagination.go
@@ -59,7 +59,11 @@ func (m *Model) pageDown() {
 	maxPageIndex := m.MaxPages() - 1
 
 	if m.currentPage > maxPageIndex {
-		m.currentPage = 0
+		if m.paginationWrapping {
+			m.currentPage = 0
+		} else {
+			m.currentPage = maxPageIndex
+		}
 	}
 
 	m.rowCursorIndex = m.currentPage * m.pageSize
@@ -75,7 +79,11 @@ func (m *Model) pageUp() {
 	maxPageIndex := m.MaxPages() - 1
 
 	if m.currentPage < 0 {
-		m.currentPage = maxPageIndex
+		if m.paginationWrapping {
+			m.currentPage = maxPageIndex
+		} else {
+			m.currentPage = 0
+		}
 	}
 
 	m.rowCursorIndex = m.currentPage * m.pageSize
diff --git a/table/query.go b/table/query.go
index b8158cb..16b08fd 100644
--- a/table/query.go
+++ b/table/query.go
@@ -66,3 +66,10 @@ func (m *Model) GetHorizontalScrollColumnOffset() int {
 func (m *Model) GetHeaderVisibility() bool {
 	return m.headerVisible
 }
+
+// GetPaginationWrapping returns true if pagination wrapping is enabled, or false
+// if disabled.  If disabled, navigating through pages will stop at the first
+// and last pages.
+func (m *Model) GetPaginationWrapping() bool {
+	return m.paginationWrapping
+}


Test patch:
diff --git a/table/query_test.go b/table/query_test.go
index 9477d20..1115073 100644
--- a/table/query_test.go
+++ b/table/query_test.go
@@ -170,3 +170,13 @@ func TestGetHeaderVisibility(t *testing.T) {
 
 	assert.False(t, model.GetHeaderVisibility(), "Header was not set to hidden")
 }
+
+func TestGetPaginationWrapping(t *testing.T) {
+	model := New([]Column{})
+
+	assert.True(t, model.GetPaginationWrapping(), "Pagination wrapping should default to true")
+
+	model = model.WithPaginationWrapping(false)
+
+	assert.False(t, model.GetPaginationWrapping(), "Pagination wrapping setting did not update after setting option")
+}
diff --git a/table/update_test.go b/table/update_test.go
index 4a56cf1..b4b144a 100644
--- a/table/update_test.go
+++ b/table/update_test.go
@@ -151,6 +151,15 @@ func TestFocusedMovesWhenMoveKeysPressedPaged(t *testing.T) {
 
 	model, _ = model.Update(keyEnd)
 	assert.Equal(t, "third", curID(), "Hitting end a second time should not move pages")
+
+	// Disable pagination wrapping and ensure it sticks
+	model = model.WithPaginationWrapping(false)
+	model, _ = model.Update(keyRight)
+	assert.Equal(t, "third", curID(), "Did not stay on last page, may have wrapped")
+
+	model, _ = model.Update(keyHome)
+	model, _ = model.Update(keyLeft)
+	assert.Equal(t, "first", curID(), "Did not stay on first page, may have wrapped")
 }
 
 func TestFocusedMovesWithCustomKeyMap(t *testing.T) {