Custom navigation from context
FolioNav is convenient, but sometimes you want your own controls — a floating
button, a step label, a "skip to end" link. Any descendant of <Folio> can call
use_folio_context() and drive navigation directly.
The context
#![allow(unused)] fn main() { pub struct FolioContext { pub current_page: ReadSignal<usize>, // reactive index pub total_pages: Signal<usize>, // reactive length pub go_next: Arc<dyn Fn() + Send + Sync>, // forward one (clamped) pub go_prev: Arc<dyn Fn() + Send + Sync>, // back one (clamped) pub go_to: Arc<dyn Fn(usize) + Send + Sync>, // jump (clamped) pub anim_epoch: ReadSignal<u64>, // ticks each turn pub last_dir: ReadSignal<Option<TurnDir>>,// Forward / Backward } }
You navigate by calling the closures and read state from the signals.
A bespoke control bar
#![allow(unused)] fn main() { use leptos::prelude::*; use leptosbook::prelude::*; #[component] fn ControlBar() -> impl IntoView { let ctx = use_folio_context(); let (go_prev, go_next, go_to) = (ctx.go_prev.clone(), ctx.go_next.clone(), ctx.go_to.clone()); let at_start = move || ctx.current_page.get() == 0; let at_end = move || ctx.current_page.get() + 1 >= ctx.total_pages.get(); view! { <div class="control-bar"> <button on:click=move |_| go_prev() disabled=at_start>"‹"</button> <span>{move || format!("{} of {}", ctx.current_page.get() + 1, ctx.total_pages.get())}</span> <button on:click=move |_| go_next() disabled=at_end>"›"</button> <button class="skip" on:click=move |_| { let last = ctx.total_pages.get().saturating_sub(1); go_to(last); }>"Skip to end"</button> </div> } } }
Reacting to a turn
Use anim_epoch or current_page in an effect to fire side effects — analytics,
autoplay pausing, lazy fetches:
#![allow(unused)] fn main() { let ctx = use_folio_context(); Effect::new(move |_| { let page = ctx.current_page.get(); // re-runs on every turn track_event("slide_view", page); }); }
And last_dir tells you the direction of the most recent turn:
#![allow(unused)] fn main() { let dir = move || match ctx.last_dir.get() { Some(TurnDir::Forward) => "→", Some(TurnDir::Backward) => "←", None => "·", }; }
Clone before you move
Each handler closure that uses a context closure needs its own clone — they're
Arcs, so cloning is cheap:
#![allow(unused)] fn main() { let go_next = ctx.go_next.clone(); // once per handler view! { <button on:click=move |_| go_next()>"Next"</button> } }