### Install Development Tools (Bash) Source: https://github.com/synphonyte/leptos-windowing/blob/main/leptos-pagination/examples/rest_api/README.md Installs necessary development tools including Trunk for Rust web development, Tailwind CSS for styling, and the wasm32-unknown-unknown target for WebAssembly compilation. ```bash cargo install trunk npm install -D tailwindcss rustup target add wasm32-unknown-unknown ``` -------------------------------- ### Watch Tailwind CSS (Bash) Source: https://github.com/synphonyte/leptos-windowing/blob/main/leptos-pagination/examples/rest_api/README.md Starts the Tailwind CSS watcher to automatically compile CSS changes from input.css to output.css whenever the source files are modified. ```bash npx tailwindcss -i ./input.css -o ./style/output.css --watch ``` -------------------------------- ### Serve Leptos Application (Bash) Source: https://github.com/synphonyte/leptos-windowing/blob/main/leptos-pagination/examples/rest_api/README.md Starts the Trunk development server for the Leptos application, which includes live reloading and automatically opens the application in the default web browser. ```bash trunk serve --open ``` -------------------------------- ### Implement pagination with use_pagination in Rust Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The use_pagination hook manages data windowing and caching. It requires a loader implementation and returns an ItemWindow to track the visible range and item states. ```rust use leptos::prelude::*; use leptos_pagination::{ use_pagination, UsePaginationOptions, PaginationState, MemoryLoader, ItemWindow, }; use leptos_windowing::item_state::ItemState; use reactive_stores::{Store, StoreFieldIterator}; use std::ops::Range; pub struct Item { pub name: String } pub struct ItemLoader; impl MemoryLoader for ItemLoader { type Item = Item; type Query = (); fn load_items(&self, range: Range, _: &()) -> Vec { vec![] } fn item_count(&self, _: &()) -> usize { 50 } } #[component] pub fn CustomPaginatedList() -> impl IntoView { let state = PaginationState::new_store(); // Get the item window with cached items let window: ItemWindow = use_pagination( state, ItemLoader, (), // query signal 20, // items per page UsePaginationOptions::default() .overscan_page_count(1), // pre-load adjacent pages ); view! {
    // Iterate over the visible range { move || { // Access cached item state match &*window.cache.items().at_unkeyed(index).read() { ItemState::Loaded(item) => { view! {
  • {item.name.clone()}
  • }.into_any() } ItemState::Loading => { view! {
  • "Loading..."
  • }.into_any() } ItemState::Error(err) => { view! {
  • {err.clone()}
  • }.into_any() } ItemState::Placeholder => { view! {
  • }.into_any() } } } }
} } ``` -------------------------------- ### Manage Pagination State with PaginationState Store Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The PaginationState store provides a reactive interface to track current page, total pages, and navigation status. It allows for programmatic control over pagination via helper methods. ```rust use leptos::prelude::*; use leptos_pagination::PaginationState; use reactive_stores::Store; #[component] pub fn PaginationExample() -> impl IntoView { let state: Store = PaginationState::new_store(); let current_page = move || state.current_page().get(); let page_count = move || state.page_count().get(); let go_next = move |_| PaginationState::next(state); let go_prev = move |_| PaginationState::prev(state); let go_to_page = move |page: usize| state.current_page().set(page); let is_first = move || PaginationState::is_first_page(state); let is_last = move || PaginationState::is_last_page(state); view! {
"Page " {move || current_page() + 1} " of " {move || page_count().map(|c| c.to_string()).unwrap_or("?".to_string())}
} } ``` -------------------------------- ### Implement Pagination with PaginatedFor Component Source: https://github.com/synphonyte/leptos-windowing/blob/main/leptos-pagination/README.md Demonstrates how to use the PaginatedFor component alongside navigation controls to display paginated data in a Leptos view. It requires a defined loader and a pagination state store. ```rust pub struct Book { title: String, } // Implement one of the loader traits for this struct pub struct BookLoader; let state = PaginationState::new_store(); view! {
  • {idx_book.data.title.clone()}
"Prev" "Next" } ``` -------------------------------- ### Build navigation controls with use_pagination_controls in Rust Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The use_pagination_controls hook generates page ranges and state signals for UI navigation. It simplifies rendering page buttons, separators, and handling active states. ```rust use leptos::prelude::*; use leptos_pagination::{ use_pagination_controls, UsePaginationControlsOptions, PaginationControls, PaginationState, }; use reactive_stores::Store; #[component] pub fn CustomPaginationControls(state: Store) -> impl IntoView { let PaginationControls { current_page, // Signal - current page (0-indexed) start_range, // Signal> - pages at beginning current_range, // Signal> - pages around current end_range, // Signal> - pages at end show_separator_before, // Signal - show "..." before current show_separator_after, // Signal - show "..." after current page_count_error, // Signal> - error message } = use_pagination_controls( state, UsePaginationControlsOptions::default() .display_page_count(5) // pages around current .margin_page_count(1), // pages at edges ); view! { // Show error if page count couldn't be determined {move || page_count_error.get().map(|e| view! { {e} })} // Render start pages (e.g., [1]) // Separator before current range "..." // Render current range pages // Separator after current range "..." // Render end pages (e.g., [10]) } } ``` -------------------------------- ### CacheController for Mutations in Leptos Source: https://context7.com/synphonyte/leptos-windowing/llms.txt Demonstrates how to use `CacheController` to provide write access for updating, inserting, or removing items in a paginated list without triggering full data reloads. This is useful for direct manipulation of cached data within a Leptos application. ```rust use leptos::prelude::*; use leptos_pagination::{ PaginatedFor, PaginationState, MemoryLoader, cache::CacheController, }; use std::ops::Range; pub struct Todo { pub id: u32, pub text: String, pub completed: bool, } pub struct TodoLoader; impl MemoryLoader for TodoLoader { type Item = Todo; type Query = (); fn load_items(&self, range: Range, _: &()) -> Vec { vec![] } fn item_count(&self, _: &()) -> usize { 10 } } #[component] pub fn EditableTodoList() -> impl IntoView { let state = PaginationState::new_store(); // Create cache controller for mutations let cache_controller: CacheController = CacheController::new(); view! {
  • {idx_todo.data.text.clone()} // Toggle completion using WindowItem methods // Delete using WindowItem // Insert new item after this one
// Or use CacheController directly } } ``` -------------------------------- ### Implement ExactLoader for range-based data fetching Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The ExactLoader trait is designed for data sources that support precise index-based range queries. It requires implementing load_items and item_count to interface with databases using LIMIT and OFFSET clauses. ```rust use std::ops::Range; use leptos_pagination::ExactLoader; pub struct Product { pub id: u32, pub name: String, pub price: f64, } pub struct ProductLoader; impl ExactLoader for ProductLoader { type Item = Product; type Query = (); type Error = String; async fn load_items( &self, range: Range, _query: &Self::Query, ) -> Result, Self::Error> { let products: Vec = sqlx::query_as!( Product, "SELECT id, name, price FROM products LIMIT $1 OFFSET $2", range.len() as i64, range.start as i64 ) .fetch_all(&pool) .await .map_err(|e| e.to_string())?; Ok(products) } async fn item_count(&self, _query: &Self::Query) -> Result, Self::Error> { let count: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM products") .fetch_one(&pool) .await .map_err(|e| e.to_string())?; Ok(Some(count as usize)) } } ``` -------------------------------- ### Implement generic Loader for custom pagination Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The Loader trait provides a flexible interface for non-standard pagination requirements. It allows for custom chunk sizes and returns a LoadedItems struct to handle discrepancies between requested and actual loaded ranges. ```rust use std::ops::Range; use leptos_pagination::{Loader, LoadedItems}; pub struct CustomLoader; impl Loader for CustomLoader { const CHUNK_SIZE: Option = Some(50); type Item = String; type Query = (); type Error = String; async fn load_items( &self, range: Range, _query: &Self::Query, ) -> Result, Self::Error> { let items: Vec = fetch_items(range.clone()).await?; Ok(LoadedItems { items, range: range.clone(), }) } async fn item_count(&self, _query: &Self::Query) -> Result, Self::Error> { Ok(Some(1000)) } } ``` -------------------------------- ### Render Paginated Lists with PaginatedFor Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The PaginatedFor component provides a high-level interface for rendering paginated data. It supports custom loaders, loading skeletons, empty states, and error handling, while integrating with navigation components. ```rust use leptos::prelude::*; use leptos_pagination::{ PaginatedFor, PaginationState, PaginationPrev, PaginationNext, PaginationPages, Loading, Empty, LoadError, MemoryLoader, }; use std::ops::Range; pub struct Book { pub title: String, pub author: String, } pub struct BookLoader; impl MemoryLoader for BookLoader { type Item = Book; type Query = (); fn load_items(&self, range: Range, _: &()) -> Vec { vec![] } fn item_count(&self, _: &()) -> usize { 100 } } #[component] pub fn BookList() -> impl IntoView { let state = PaginationState::new_store(); view! {
  • {idx_book.data.title.clone()}

    {idx_book.data.author.clone()}

  • No books found
  • Failed to load: {error}
} } ``` -------------------------------- ### Implement MemoryLoader for Synchronous Data Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The MemoryLoader trait allows for efficient, synchronous access to in-memory data structures like vectors. It requires implementing item retrieval based on ranges and a total count method, supporting optional query filtering. ```rust use std::ops::Range; use leptos_pagination::MemoryLoader; pub struct Book { pub title: String, pub author: String, } pub struct BookLoader { books: Vec, } impl MemoryLoader for BookLoader { type Item = Book; type Query = (); fn load_items(&self, range: Range, _query: &Self::Query) -> Vec { self.books[range.clone()].to_vec() } fn item_count(&self, _query: &Self::Query) -> usize { self.books.len() } } pub struct FilteredBookLoader { books: Vec, } impl MemoryLoader for FilteredBookLoader { type Item = Book; type Query = String; fn load_items(&self, range: Range, query: &Self::Query) -> Vec { let filtered: Vec<_> = self.books .iter() .filter(|b| b.title.to_lowercase().contains(&query.to_lowercase())) .cloned() .collect(); filtered.get(range).unwrap_or(&[]).to_vec() } fn item_count(&self, query: &Self::Query) -> usize { self.books .iter() .filter(|b| b.title.to_lowercase().contains(&query.to_lowercase())) .count() } } ``` -------------------------------- ### Implement PaginatedLoader for Asynchronous APIs Source: https://context7.com/synphonyte/leptos-windowing/llms.txt The PaginatedLoader trait is designed for fetching data from remote REST APIs. It handles asynchronous page requests and provides metadata about the total item count to support UI pagination controls. ```rust use leptos_pagination::{PaginatedLoader, PaginatedCount}; use gloo_net::http::Request; use serde::Deserialize; #[derive(Deserialize, Clone)] pub struct Brewery { pub id: String, pub name: String, pub city: Option, pub country: Option, pub website_url: Option, } #[derive(Default, Clone)] pub struct BreweryQuery { pub sort_by: Option, pub sort_order: Option, } pub struct BreweryLoader; impl PaginatedLoader for BreweryLoader { const PAGE_ITEM_COUNT: usize = 20; type Item = Brewery; type Query = BreweryQuery; type Error = anyhow::Error; async fn load_page( &self, page_index: usize, query: &Self::Query, ) -> Result, Self::Error> { let mut url = format!( "https://api.openbrewerydb.org/v1/breweries?page={}&per_page={}", page_index + 1, Self::PAGE_ITEM_COUNT ); if let Some(ref sort_by) = query.sort_by { url.push_str(&format!("&sort={}:{}", sort_by, query.sort_order.as_deref().unwrap_or("asc") )); } let resp: Vec = Request::get(&url) .send() .await? .json() .await?; Ok(resp) } async fn count(&self, _query: &Self::Query) -> Result, Self::Error> { #[derive(Deserialize)] struct MetaResponse { total: usize } let resp: MetaResponse = Request::get("https://api.openbrewerydb.org/v1/breweries/meta") .send() .await? .json() .await?; Ok(Some(PaginatedCount::Items(resp.total))) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.