|
| 1 | +// leptos-struct-table/src/components/table_content.rs |
| 2 | + |
1 | 3 | #![allow(clippy::await_holding_refcell_ref)] |
2 | 4 |
|
3 | 5 | use crate::components::renderer_fn::renderer_fn; |
@@ -167,7 +169,7 @@ pub fn TableContent<Row, DataP, Err, ClsP, ScrollEl, ScrollM>( |
167 | 169 | /// Can be one of |
168 | 170 | /// - `Virtualization` |
169 | 171 | /// - `InfiniteScroll` |
170 | | - /// - `Pagination` |
| 172 | + /// - `Pagination` |
171 | 173 | /// |
172 | 174 | /// Please check [`DisplayStrategy`] to see explanations of all available options. |
173 | 175 | #[prop(optional)] |
@@ -394,63 +396,74 @@ where |
394 | 396 | ); |
395 | 397 |
|
396 | 398 | Effect::new(move || { |
397 | | - let first_visible_row_index = first_visible_row_index.get(); |
398 | | - let visible_row_count = visible_row_count.get().min(MAX_DISPLAY_ROW_COUNT); |
399 | | - |
400 | 399 | // with this a reload triggers this effect |
401 | 400 | reload_count.track(); |
402 | 401 |
|
403 | | - if visible_row_count == 0 { |
404 | | - return; |
405 | | - } |
406 | | - |
407 | | - let mut start = first_visible_row_index.saturating_sub(visible_row_count * 2); |
408 | | - |
409 | | - let mut end = start + visible_row_count * 5; |
| 402 | + // 1. Get all values *atomically* within a single .with() call |
| 403 | + let (first_visible, visible_count, row_count_opt) = loaded_rows.with(|_| { |
| 404 | + ( |
| 405 | + first_visible_row_index.get(), |
| 406 | + visible_row_count.get(), |
| 407 | + row_count.get(), |
| 408 | + ) |
| 409 | + }); |
410 | 410 |
|
411 | | - if let Some(chunk_size) = DataP::CHUNK_SIZE { |
412 | | - start /= chunk_size; |
413 | | - start *= chunk_size; |
| 411 | + let visible_count = visible_count.min(MAX_DISPLAY_ROW_COUNT); |
414 | 412 |
|
415 | | - end /= chunk_size; |
416 | | - end += 1; |
417 | | - end *= chunk_size; |
| 413 | + if visible_count == 0 { |
| 414 | + return; |
418 | 415 | } |
419 | 416 |
|
420 | | - if let Some(row_count) = row_count.get() { |
| 417 | + let mut start = first_visible.saturating_sub(visible_count * 2); |
| 418 | + let mut end = start + visible_count * 5; |
| 419 | + |
| 420 | + if let Some(row_count) = row_count_opt { |
| 421 | + // Clamp end to row_count if we know it |
421 | 422 | end = end.min(row_count); |
422 | | - } |
423 | 423 |
|
424 | | - if !matches!(display_strategy, DisplayStrategy::Pagination { .. }) { |
425 | | - end = end.min(start + MAX_DISPLAY_ROW_COUNT); |
| 424 | + // Ensure start is within valid bounds *after* clamping end |
| 425 | + start = start.min(end); // Crucial: prevent start > end |
| 426 | + } else { |
| 427 | + //If total number of rows is unknown, we don't clamp, |
| 428 | + // but limit to MAX_DISPLAY_ROW_COUNT |
| 429 | + if !matches!(display_strategy, DisplayStrategy::Pagination { .. }) { |
| 430 | + end = end.min(start + MAX_DISPLAY_ROW_COUNT); |
| 431 | + } |
426 | 432 | } |
427 | 433 |
|
428 | | - loaded_rows.update_untracked(|loaded_rows| { |
429 | | - if end > loaded_rows.len() { |
430 | | - loaded_rows.resize(end); |
431 | | - } |
432 | | - }); |
| 434 | + if let Some(chunk_size) = DataP::CHUNK_SIZE { |
| 435 | + start = (start / chunk_size) * chunk_size; |
| 436 | + end = ((end + chunk_size - 1) / chunk_size) * chunk_size; // Round end *up* to nearest chunk size |
| 437 | + } |
433 | 438 |
|
434 | 439 | let range = start..end; |
435 | 440 |
|
436 | 441 | set_display_range.set(match display_strategy { |
437 | 442 | DisplayStrategy::Virtualization | DisplayStrategy::InfiniteScroll => range.clone(), |
438 | 443 | DisplayStrategy::Pagination { row_count, .. } => { |
439 | | - first_visible_row_index..(first_visible_row_index + row_count).min(end) |
| 444 | + first_visible..(first_visible + row_count).min(end) |
| 445 | + } |
| 446 | + }); |
| 447 | + |
| 448 | + loaded_rows.update_untracked(|loaded_rows| { |
| 449 | + if end > loaded_rows.len() { |
| 450 | + loaded_rows.resize(end); |
440 | 451 | } |
441 | 452 | }); |
442 | 453 |
|
443 | 454 | let missing_range = |
444 | 455 | loaded_rows.with_untracked(|loaded_rows| loaded_rows.missing_range(range.clone())); |
445 | 456 |
|
446 | 457 | if let Some(missing_range) = missing_range { |
447 | | - let mut end = missing_range.end; |
448 | | - if let Some(row_count) = row_count.get() { |
449 | | - end = end.min(row_count); |
| 458 | + // Ensure missing_range is valid *after* all calculations |
| 459 | + let missing_start = missing_range.start.min(missing_range.end); |
| 460 | + let missing_end = missing_range.end; // Already correct |
450 | 461 |
|
451 | | - if end <= missing_range.start { |
452 | | - return; |
453 | | - } |
| 462 | + let missing_range = missing_start..missing_end; |
| 463 | + |
| 464 | + if missing_range.is_empty() { |
| 465 | + // Don't proceed with empty ranges |
| 466 | + return; |
454 | 467 | } |
455 | 468 |
|
456 | 469 | loaded_rows.write().write_loading(missing_range.clone()); |
@@ -497,12 +510,16 @@ where |
497 | 510 |
|
498 | 511 | if let Ok((_, loaded_range)) = &result { |
499 | 512 | if loaded_range.end < missing_range.end { |
500 | | - if let Some(row_count) = row_count.get_untracked() { |
501 | | - if loaded_range.end < row_count { |
| 513 | + match row_count_opt { // Use pre-fetched value! |
| 514 | + Some(row_count) => { |
| 515 | + if loaded_range.end < row_count { |
| 516 | + set_known_row_count(loaded_range.end); |
| 517 | + } |
| 518 | + }, |
| 519 | + None => { |
502 | 520 | set_known_row_count(loaded_range.end); |
503 | | - } |
504 | | - } else { |
505 | | - set_known_row_count(loaded_range.end); |
| 521 | + }, |
| 522 | + |
506 | 523 | } |
507 | 524 | } |
508 | 525 | } |
|
0 commit comments