Skip to content

Commit 9d1ad1b

Browse files
committed
added reactive data tracking to data providers
1 parent 67b66b2 commit 9d1ad1b

File tree

9 files changed

+115
-23
lines changed

9 files changed

+115
-23
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.8.2] - 2024-02-18
4+
5+
### Feature 🚀
6+
7+
- Added method `TableDataProvider::track` to easily specify reactive dependencies of data loading
8+
39
## [0.8.1] - 2024-02-17
410

511
### Fix 🐛

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "leptos-struct-table"
3-
version = "0.8.1"
3+
version = "0.8.2"
44
edition = "2021"
55
authors = ["Marc-Stefan Cassola"]
66
categories = ["gui", "web-programming"]

examples/paginated_rest_datasource/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
leptos = { version = "0.6", features = ["nightly", "csr"] }
8+
leptos-use = "0.10"
89
leptos-struct-table = { path = "../..", features = ["chrono"] }
910
console_error_panic_hook = "0.1"
1011
console_log = "1"

examples/paginated_rest_datasource/src/data_provider.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
use crate::models::{ArchiveOrgApiResponse, ArchiveOrgCountRespone, Book};
22
use async_trait::async_trait;
33
use gloo_net::http::Request;
4-
use leptos::logging;
4+
use leptos::*;
55
use leptos_struct_table::{ColumnSort, PaginatedTableDataProvider};
66
use std::collections::VecDeque;
77

88
pub struct BookDataProvider {
99
sorting: VecDeque<(usize, ColumnSort)>,
10+
pub search: RwSignal<String>,
1011
}
1112

1213
impl Default for BookDataProvider {
1314
fn default() -> Self {
1415
Self {
1516
sorting: VecDeque::new(),
17+
search: RwSignal::new("lewis".to_string()),
1618
}
1719
}
1820
}
@@ -47,14 +49,15 @@ impl BookDataProvider {
4749
}
4850

4951
format!(
50-
"https://archive.org/advancedsearch.php?q=creator%3A%28Lewis%29&fl%5B%5D=creator&fl%5B%5D=identifier&fl%5B%5D=publicdate&fl%5B%5D=title{sort}&rows={}&page={}&output=json&callback=",
52+
"https://archive.org/advancedsearch.php?q=creator%3A%28{}%29&fl%5B%5D=creator&fl%5B%5D=identifier&fl%5B%5D=publicdate&fl%5B%5D=title{sort}&rows={}&page={}&output=json&callback=",
53+
self.search.get_untracked(),
5154
Self::PAGE_ROW_COUNT,
5255
page_index + 1,
5356
)
5457
}
5558
}
5659

57-
#[async_trait(?Send)]
60+
#[async_trait(? Send)]
5861
impl PaginatedTableDataProvider<Book> for BookDataProvider {
5962
const PAGE_ROW_COUNT: usize = 50;
6063

@@ -97,4 +100,9 @@ impl PaginatedTableDataProvider<Book> for BookDataProvider {
97100
fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
98101
self.sorting = sorting.clone();
99102
}
103+
104+
fn track(&self) {
105+
// we depend on the search so we need to track it here
106+
self.search.track();
107+
}
100108
}

examples/paginated_rest_datasource/src/main.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::data_provider::BookDataProvider;
66
use leptos::html::Div;
77
use leptos::*;
88
use leptos_struct_table::*;
9+
use leptos_use::use_debounce_fn_with_arg;
910

1011
#[component]
1112
pub fn App() -> impl IntoView {
@@ -21,10 +22,24 @@ pub fn App() -> impl IntoView {
2122

2223
let (count, set_count) = create_signal(0);
2324

25+
let on_input = use_debounce_fn_with_arg(move |value| rows.search.set(value), 300.0);
26+
2427
view! {
2528
<div class="container">
2629
<div class="top-bar">
2730
<button on:click=reload>"Reload"</button>
31+
<div id="search">
32+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
33+
// !Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
34+
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/>
35+
</svg>
36+
<input
37+
type="search"
38+
placeholder="Search"
39+
on:input=move |e| { on_input(event_target_value(&e)); }
40+
value=rows.search
41+
/>
42+
</div>
2843
<Show when=move || { count() > 0 }>
2944
<div>"Found " {count} " results"</div>
3045
</Show>

examples/paginated_rest_datasource/style.css

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
body, html {
22
font-family: sans-serif;
3+
font-size: 11pt;
34
height: 100vh;
45
margin: 0;
56
padding: 0;
@@ -17,11 +18,37 @@ body, html {
1718
display: flex;
1819
align-items: center;
1920
justify-content: space-between;
20-
padding: 0.5rem 1rem;
21+
padding: 10px 15px;
2122
background: white;
2223
border-bottom: 1px solid silver;
2324
}
2425

26+
#search {
27+
display: flex;
28+
border: 1px solid silver;
29+
border-radius: 1000px;
30+
align-items: center;
31+
padding-left: 7px;
32+
}
33+
34+
#search > * {
35+
display: block;
36+
}
37+
38+
#search > svg {
39+
height: 16px;
40+
opacity: 0.5;
41+
}
42+
43+
input[type="search"] {
44+
min-width: 50%;
45+
border: 0 none;
46+
appearance: none;
47+
padding: 5px 15px 5px 5px;
48+
background: transparent;
49+
outline: none;
50+
}
51+
2552
.table-container {
2653
flex: 1;
2754
overflow: auto;
@@ -42,7 +69,8 @@ th {
4269
position: sticky;
4370
top: 0;
4471
width: 20%;
45-
padding: 5px 0;
72+
padding: 10px 15px;
73+
text-align: left;
4674
border-bottom: 1px solid silver;
4775
}
4876

@@ -55,7 +83,7 @@ td {
5583
overflow: hidden;
5684
text-overflow: ellipsis;
5785
width: 20%;
58-
padding: 4px 5px;
86+
padding: 10px 15px;
5987
}
6088

6189
td:first-child {

src/components/table_content.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,22 @@ where
187187

188188
let first_selected_index = create_rw_signal(None::<usize>);
189189

190-
let (do_reload, set_reload) = create_signal(false);
191-
let clear = move || {
190+
let (row_count, set_row_count) = create_signal(None::<usize>);
191+
192+
let (reload_count, set_reload_count) = create_signal(0_usize);
193+
let clear = move |clear_row_count: bool| {
192194
selection.clear();
193195
first_selected_index.set(None);
194196

195197
loaded_rows.update(|loaded_rows| {
196198
loaded_rows.clear();
197199
});
198200

199-
set_reload.set(true);
201+
if clear_row_count {
202+
set_row_count.set(None);
203+
}
204+
205+
set_reload_count.set(reload_count.get().overflowing_add(1).0);
200206
};
201207

202208
let on_head_click = {
@@ -207,14 +213,19 @@ where
207213

208214
rows.borrow_mut().set_sorting(&sorting());
209215

210-
clear();
216+
clear(false);
211217
}
212218
};
213219

214-
create_effect(move |_| {
215-
// triggered when `ReloadController::reload()` is called
216-
reload_controller.track();
217-
clear();
220+
create_effect({
221+
let rows = Rc::clone(&rows);
222+
223+
move |_| {
224+
// triggered when `ReloadController::reload()` is called
225+
reload_controller.track();
226+
rows.borrow().track();
227+
clear(true);
228+
}
218229
});
219230

220231
let selected_indices = match selection {
@@ -238,8 +249,6 @@ where
238249
UseElementSizeOptions::default().box_(web_sys::ResizeObserverBoxOptions::ContentBox),
239250
);
240251

241-
let (row_count, set_row_count) = create_signal(None::<usize>);
242-
243252
let set_known_row_count = move |row_count: usize| {
244253
set_row_count.set(Some(row_count));
245254
loaded_rows.update(|loaded_rows| loaded_rows.resize(row_count));
@@ -335,15 +344,13 @@ where
335344
let first_visible_row_index = first_visible_row_index.get();
336345
let visible_row_count = visible_row_count.get();
337346

347+
// with this a reload triggers this effect
348+
reload_count.track();
349+
338350
if visible_row_count == 0 {
339351
return;
340352
}
341353

342-
// with this a reload triggers this effect
343-
if do_reload.get() {
344-
set_reload.set_untracked(false);
345-
}
346-
347354
let mut start = first_visible_row_index.saturating_sub(visible_row_count * 2);
348355

349356
let mut end = start + visible_row_count * 5;
@@ -411,12 +418,19 @@ where
411418
let set_known_row_count = set_known_row_count.clone();
412419

413420
async move {
421+
let latest_reload_count = reload_count.get_untracked();
422+
414423
let result = rows
415424
.borrow()
416425
.get_rows(missing_range.clone())
417426
.await
418427
.map_err(|err| format!("{err:?}"));
419428

429+
// make sure the loaded data is still valid
430+
if reload_count.get_untracked() != latest_reload_count {
431+
return;
432+
}
433+
420434
if let Ok((_, loaded_range)) = &result {
421435
if loaded_range.end < missing_range.end {
422436
if let Some(row_count) = row_count.get_untracked() {

src/data_provider.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ pub trait TableDataProvider<Row, Err: Debug = String> {
5757
fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
5858
// by default do nothing
5959
}
60+
61+
/// Call `.track()` in this method on all signals that loading data relies on.
62+
/// For example a search of filters. Please check the [paginated_rest_datasource example](https://github.com/Synphonyte/leptos-struct-table/blob/master/examples/paginated_rest_datasource/src/data_provider.rs)
63+
fn track(&self) {
64+
// by default do nothing
65+
}
6066
}
6167

6268
/// A paginated data source. This is meant to provide a more convenient way
@@ -100,6 +106,11 @@ pub trait PaginatedTableDataProvider<Row, Err: Debug = String> {
100106
fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
101107
// by default do nothing
102108
}
109+
110+
/// Same as [`TableDataProvider::track`]
111+
fn track(&self) {
112+
// by default do nothing
113+
}
103114
}
104115

105116
#[async_trait(? Send)]
@@ -129,6 +140,10 @@ where
129140
fn set_sorting(&mut self, sorting: &VecDeque<(usize, ColumnSort)>) {
130141
PaginatedTableDataProvider::<Row, Err>::set_sorting(self, sorting)
131142
}
143+
144+
fn track(&self) {
145+
PaginatedTableDataProvider::<Row, Err>::track(self)
146+
}
132147
}
133148

134149
/// Return `vec[range.start..range.end]` where `range` is clamped to the length of `vec`.

src/loaded_rows.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ impl<T: Clone> LoadedRows<T> {
6666
}
6767
}
6868
Err(error) => {
69-
for row in &mut self.rows[missing_range] {
69+
let range = missing_range.start..missing_range.end.min(self.rows.len());
70+
if range.start >= range.end {
71+
return;
72+
}
73+
74+
for row in &mut self.rows[range] {
7075
*row = RowState::Error(error.clone());
7176
}
7277
}

0 commit comments

Comments
 (0)