feat: Pagination support for jumping 20 items at a time in all table views [#45]
This commit is contained in:
@@ -49,6 +49,11 @@ pub trait Scrollable {
|
||||
fn scroll_to_bottom(&mut self);
|
||||
}
|
||||
|
||||
pub trait Paginated {
|
||||
fn page_down(&mut self);
|
||||
fn page_up(&mut self);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScrollableText {
|
||||
pub items: Vec<String>,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::models::stateful_list::StatefulList;
|
||||
use crate::models::{strip_non_search_characters, HorizontallyScrollableText, Scrollable};
|
||||
use crate::models::{
|
||||
strip_non_search_characters, HorizontallyScrollableText, Paginated, Scrollable,
|
||||
};
|
||||
use ratatui::widgets::TableState;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
@@ -151,6 +153,79 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Paginated for StatefulTable<T>
|
||||
where
|
||||
T: Clone + PartialEq + Eq + Debug,
|
||||
{
|
||||
fn page_down(&mut self) {
|
||||
if let Some(filtered_items) = self.filtered_items.as_ref() {
|
||||
if filtered_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.filtered_state.as_ref().unwrap().selected() {
|
||||
Some(i) => {
|
||||
self
|
||||
.filtered_state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.select(Some(i.saturating_add(20) % (filtered_items.len() - 1)));
|
||||
}
|
||||
None => self.filtered_state.as_mut().unwrap().select_first(),
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if self.items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.state.selected() {
|
||||
Some(i) => {
|
||||
self
|
||||
.state
|
||||
.select(Some(i.saturating_add(20) % (self.items.len() - 1)));
|
||||
}
|
||||
None => self.state.select_first(),
|
||||
};
|
||||
}
|
||||
|
||||
fn page_up(&mut self) {
|
||||
if let Some(filtered_items) = self.filtered_items.as_ref() {
|
||||
if filtered_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.filtered_state.as_ref().unwrap().selected() {
|
||||
Some(i) => {
|
||||
let len = filtered_items.len() - 1;
|
||||
self
|
||||
.filtered_state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.select(Some((i + len - (20 % len)) % len));
|
||||
}
|
||||
None => self.filtered_state.as_mut().unwrap().select_last(),
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if self.items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.state.selected() {
|
||||
Some(i) => {
|
||||
let len = self.items.len() - 1;
|
||||
self.state.select(Some((i + len - (20 % len)) % len));
|
||||
}
|
||||
None => self.state.select_last(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> StatefulTable<T>
|
||||
where
|
||||
T: Clone + PartialEq + Eq + Debug + Default,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::stateful_table::{SortOption, StatefulTable};
|
||||
use crate::models::Scrollable;
|
||||
use crate::models::{Paginated, Scrollable};
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use ratatui::widgets::TableState;
|
||||
use std::iter;
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_scrolling_on_empty_table_performs_no_op() {
|
||||
@@ -190,6 +191,174 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_pagination_on_empty_table_performs_no_op() {
|
||||
let mut stateful_table: StatefulTable<String> = StatefulTable::default();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), None);
|
||||
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), None);
|
||||
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_filtered_pagination_on_empty_table_performs_no_op() {
|
||||
let mut filtered_stateful_table: StatefulTable<String> = StatefulTable {
|
||||
filtered_items: Some(Vec::new()),
|
||||
filtered_state: Some(TableState::default()),
|
||||
..StatefulTable::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
filtered_stateful_table
|
||||
.filtered_state
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.selected(),
|
||||
None
|
||||
);
|
||||
|
||||
filtered_stateful_table.page_down();
|
||||
|
||||
assert_eq!(
|
||||
filtered_stateful_table
|
||||
.filtered_state
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.selected(),
|
||||
None
|
||||
);
|
||||
|
||||
filtered_stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
filtered_stateful_table
|
||||
.filtered_state
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.selected(),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_pagination() {
|
||||
let mut stateful_table = StatefulTable::default();
|
||||
let mut curr = 0;
|
||||
stateful_table.set_filtered_items(
|
||||
iter::repeat_with(|| {
|
||||
let tmp = curr;
|
||||
curr += 1;
|
||||
tmp
|
||||
})
|
||||
.take(100)
|
||||
.collect(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(20)
|
||||
);
|
||||
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(stateful_table.filtered_items.as_ref().unwrap().len() - 21)
|
||||
);
|
||||
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
stateful_table.scroll_down();
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(stateful_table.filtered_items.as_ref().unwrap().len() - 20)
|
||||
);
|
||||
|
||||
stateful_table.scroll_down();
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.filtered_state.as_ref().unwrap().selected(),
|
||||
Some(2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_filtered_items_pagination() {
|
||||
let mut stateful_table = StatefulTable::default();
|
||||
let mut curr = 0;
|
||||
stateful_table.set_items(
|
||||
iter::repeat_with(|| {
|
||||
let tmp = curr;
|
||||
curr += 1;
|
||||
tmp
|
||||
})
|
||||
.take(100)
|
||||
.collect(),
|
||||
);
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), Some(0));
|
||||
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), Some(20));
|
||||
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), Some(0));
|
||||
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.state.selected(),
|
||||
Some(stateful_table.items.len() - 21)
|
||||
);
|
||||
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), Some(0));
|
||||
|
||||
stateful_table.scroll_down();
|
||||
stateful_table.page_up();
|
||||
|
||||
assert_eq!(
|
||||
stateful_table.state.selected(),
|
||||
Some(stateful_table.items.len() - 20)
|
||||
);
|
||||
|
||||
stateful_table.scroll_down();
|
||||
stateful_table.page_down();
|
||||
|
||||
assert_eq!(stateful_table.state.selected(), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stateful_table_set_items() {
|
||||
let items_vec = vec!["Test 1", "Test 2", "Test 3"];
|
||||
|
||||
Reference in New Issue
Block a user