diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs
index 91118801..17bfc95b 100644
--- a/crates/story/src/table_story.rs
+++ b/crates/story/src/table_story.rs
@@ -1,12 +1,16 @@
+use std::time::Duration;
+
use fake::Fake;
use gpui::{
- div, img, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, View, ViewContext,
- VisualContext as _, WindowContext,
+ div, img, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, Timer, View,
+ ViewContext, VisualContext as _, WindowContext,
};
use ui::{
checkbox::Checkbox,
h_flex,
+ indicator::Indicator,
label::Label,
+ prelude::FluentBuilder as _,
table::{ColSort, Table, TableDelegate, TableEvent},
theme::ActiveTheme as _,
v_flex, Icon, IconName, Selectable,
@@ -84,6 +88,8 @@ struct CustomerTableDelegate {
col_order: bool,
col_sort: bool,
col_selection: bool,
+ loading: bool,
+ is_eof: bool,
}
impl CustomerTableDelegate {
@@ -111,6 +117,8 @@ impl CustomerTableDelegate {
col_order: true,
col_sort: true,
col_selection: true,
+ loading: false,
+ is_eof: false,
}
}
}
@@ -322,6 +330,32 @@ impl TableDelegate for CustomerTableDelegate {
}
}
}
+
+ fn can_load_more(&self) -> bool {
+ return !self.loading && !self.is_eof;
+ }
+
+ fn load_more_threshold(&self) -> usize {
+ 150
+ }
+
+ fn load_more(&mut self, cx: &mut ViewContext
>) {
+ self.loading = true;
+
+ cx.spawn(|view, mut cx| async move {
+ // Simulate network request, delay 1s to load data.
+ Timer::after(Duration::from_secs(1)).await;
+
+ cx.update(|cx| {
+ let _ = view.update(cx, |view, _| {
+ view.delegate_mut().customers.extend(randome_customers(200));
+ view.delegate_mut().loading = false;
+ view.delegate_mut().is_eof = view.delegate().customers.len() >= 6000;
+ });
+ })
+ })
+ .detach();
+ }
}
pub struct TableStory {
@@ -438,7 +472,12 @@ impl Render for TableStory {
.label("Column Selection")
.selected(delegate.col_selection)
.on_click(cx.listener(Self::toggle_col_selection)),
- ),
+ )
+ .when(delegate.loading, |this| {
+ this.child(h_flex().gap_1().child(Indicator::new()).child("Loading..."))
+ })
+ .child(format!("Total Rows: {}", delegate.rows_count()))
+ .when(delegate.is_eof, |this| this.child("Is loaded all data.")),
)
.child(self.table.clone())
}
diff --git a/crates/ui/src/table.rs b/crates/ui/src/table.rs
index 40799fd5..0359fcfa 100644
--- a/crates/ui/src/table.rs
+++ b/crates/ui/src/table.rs
@@ -1,4 +1,4 @@
-use std::{cell::Cell, rc::Rc};
+use std::{cell::Cell, ops::Range, rc::Rc};
use crate::{
h_flex,
@@ -198,6 +198,29 @@ pub trait TableDelegate: Sized + 'static {
.child(Icon::new(IconName::Inbox).size_12())
.into_any_element()
}
+
+ /// Return true to enable load more data when scrolling to the bottom.
+ ///
+ /// Default: true
+ fn can_load_more(&self) -> bool {
+ true
+ }
+
+ /// Returns a threshold value (n rows), of course, when scrolling to the bottom,
+ /// the remaining number of rows triggers `load_more`.
+ ///
+ /// Default: 50 rows
+ fn load_more_threshold(&self) -> usize {
+ 50
+ }
+
+ /// Load more data when the table is scrolled to the bottom.
+ ///
+ /// This will performed in a background task.
+ ///
+ /// This is always called when the table is near the bottom,
+ /// so you must check if there is more data to load or lock the loading state.
+ fn load_more(&mut self, cx: &mut ViewContext>) {}
}
impl Table
@@ -650,6 +673,24 @@ where
})
}
+ /// Dispatch delegate's `load_more` method when the visible range is near the end.
+ fn load_more(&mut self, visible_range: Range, cx: &mut ViewContext) {
+ if !self.delegate.can_load_more() {
+ return;
+ }
+
+ if visible_range.end >= self.delegate.rows_count() - self.delegate.load_more_threshold() {
+ cx.spawn(|view, mut cx| async move {
+ cx.update(|cx| {
+ view.update(cx, |view, cx| {
+ view.delegate.load_more(cx);
+ })
+ })
+ })
+ .detach();
+ }
+ }
+
fn move_col(&mut self, col_ix: usize, to_ix: usize, cx: &mut ViewContext) {
if col_ix == to_ix {
return;
@@ -746,6 +787,8 @@ where
uniform_list(view, "table-uniform-list", rows_count, {
let horizontal_scroll_handle = horizontal_scroll_handle.clone();
move |table, visible_range, cx| {
+ table.load_more(visible_range.clone(), cx);
+
visible_range
.map(|row_ix| {
table