Skip to main content

fenrix_router/
lib.rs

1use fenrix_core::{create_signal, provide_service, inject};
2use std::collections::HashMap;
3use std::rc::Rc;
4use wasm_bindgen::prelude::*;
5use wasm_bindgen::JsCast;
6use web_sys::{window, HashChangeEvent, Node};
7
8// A component function that returns a renderable node.
9pub type Routable = fn() -> Node;
10
11// The Router service that manages routes and the current path.
12#[derive(Clone)]
13pub struct Router {
14    routes: Rc<HashMap<String, Routable>>,
15    // The current path is a reactive signal.
16    pub current_path: Rc<dyn Fn() -> String>,
17    set_current_path: Rc<dyn Fn(String)>,
18}
19
20impl Router {
21    pub fn new(routes: HashMap<String, Routable>) -> Self {
22        let (current_path, set_current_path) = create_signal(get_current_hash());
23
24        let router = Self {
25            routes: Rc::new(routes),
26            current_path: Rc::new(current_path),
27            set_current_path: Rc::new(set_current_path),
28        };
29
30        // Listen for hash changes to update the current_path signal.
31        let router_clone = router.clone();
32        let on_hash_change = Closure::wrap(Box::new(move |_: HashChangeEvent| {
33            (router_clone.set_current_path)(get_current_hash());
34        }) as Box<dyn FnMut(_)>);
35
36        window()
37            .unwrap()
38            .add_event_listener_with_callback("hashchange", on_hash_change.as_ref().unchecked_ref())
39            .unwrap();
40        on_hash_change.forget();
41
42        router
43    }
44
45    // Returns the component function for the given path, or None if not found.
46    pub fn get_component(&self, path: &str) -> Option<Routable> {
47        self.routes.get(path).cloned()
48    }
49}
50
51// Helper to get the current URL hash, defaulting to "/".
52fn get_current_hash() -> String {
53    let hash = window().unwrap().location().hash().unwrap_or_else(|_| "".to_string());
54    if hash.is_empty() {
55        "/".to_string()
56    } else {
57        // Remove the leading '#'
58        hash[1..].to_string()
59    }
60}
61
62/// Provides a router instance to the application.
63pub fn provide_router(routes: HashMap<String, Routable>) {
64    provide_service(Router::new(routes));
65}
66
67/// A hook to get the current router instance from the DI container.
68pub fn use_router() -> Rc<Router> {
69    inject::<Router>()
70}