dioxus/packages/router/src/lib.rs

173 lines
5.1 KiB
Rust
Raw Normal View History

2021-11-03 04:35:56 +00:00
mod utils;
2021-12-01 03:48:05 +00:00
use std::{cell::RefCell, rc::Rc};
2021-11-03 04:35:56 +00:00
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
2021-12-01 03:48:05 +00:00
use dioxus_core_macro::{rsx, Props};
2021-11-03 04:35:56 +00:00
use dioxus_html as dioxus_elements;
2021-11-22 20:22:42 +00:00
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{window, Event};
2021-11-03 04:35:56 +00:00
2021-11-22 20:22:42 +00:00
use crate::utils::strip_slash_suffix;
2021-11-03 04:35:56 +00:00
2021-11-22 20:22:42 +00:00
pub trait Routable: 'static + Send + Clone + PartialEq {}
impl<T> Routable for T where T: 'static + Send + Clone + PartialEq {}
2021-11-19 05:49:04 +00:00
2021-11-03 04:35:56 +00:00
pub struct RouterService<R: Routable> {
2021-11-22 20:22:42 +00:00
historic_routes: Vec<R>,
history_service: RefCell<web_sys::History>,
2021-11-03 04:35:56 +00:00
base_ur: RefCell<Option<String>>,
}
impl<R: Routable> RouterService<R> {
fn push_route(&self, r: R) {
2021-11-22 20:22:42 +00:00
todo!()
// self.historic_routes.borrow_mut().push(r);
2021-11-03 04:35:56 +00:00
}
fn get_current_route(&self) -> &str {
todo!()
}
fn update_route_impl(&self, url: String, push: bool) {
let history = web_sys::window().unwrap().history().expect("no history");
let base = self.base_ur.borrow();
let path = match base.as_ref() {
Some(base) => {
let path = format!("{}{}", base, url);
if path.is_empty() {
"/".to_string()
} else {
path
}
}
None => url,
};
if push {
history
.push_state_with_url(&JsValue::NULL, "", Some(&path))
.expect("push history");
} else {
history
.replace_state_with_url(&JsValue::NULL, "", Some(&path))
.expect("replace history");
}
let event = Event::new("popstate").unwrap();
web_sys::window()
.unwrap()
.dispatch_event(&event)
.expect("dispatch");
}
}
/// This hould only be used once per app
///
/// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine
2021-12-15 02:46:19 +00:00
pub fn use_router<R: Routable>(cx: &ScopeState, mut parse: impl FnMut(&str) -> R + 'static) -> &R {
2021-11-03 04:35:56 +00:00
// for the web, attach to the history api
cx.use_hook(
|f| {
//
use gloo::events::EventListener;
2021-11-22 20:22:42 +00:00
let base = window()
.unwrap()
.document()
.unwrap()
.query_selector("base[href]")
.ok()
.flatten()
.and_then(|base| {
let base = JsCast::unchecked_into::<web_sys::HtmlBaseElement>(base).href();
let url = web_sys::Url::new(&base).unwrap();
if url.pathname() != "/" {
Some(strip_slash_suffix(&base).to_string())
} else {
None
}
});
let location = window().unwrap().location();
let pathname = location.pathname().unwrap();
let initial_route = parse(&pathname);
2021-11-03 04:35:56 +00:00
let service: RouterService<R> = RouterService {
2021-11-22 20:22:42 +00:00
historic_routes: vec![initial_route],
history_service: RefCell::new(
web_sys::window().unwrap().history().expect("no history"),
),
base_ur: RefCell::new(base),
2021-11-03 04:35:56 +00:00
};
2021-11-22 20:22:42 +00:00
// let base = base_url();
// let url = route.to_path();
// pending_routes: RefCell::new(vec![]),
2021-11-19 05:49:04 +00:00
// service.history_service.push_state(data, title);
2021-11-22 20:22:42 +00:00
// cx.provide_state(service);
2021-11-03 04:35:56 +00:00
let regenerate = cx.schedule_update();
2021-11-22 20:22:42 +00:00
// // when "back" is called by the user, we want to to re-render the component
2021-11-03 04:35:56 +00:00
let listener = EventListener::new(&web_sys::window().unwrap(), "popstate", move |_| {
//
regenerate();
});
2021-11-22 20:22:42 +00:00
service
2021-11-03 04:35:56 +00:00
},
2021-11-22 20:22:42 +00:00
|state| {
let base = state.base_ur.borrow();
if let Some(base) = base.as_ref() {
//
let path = format!("{}{}", base, state.get_current_route());
}
let history = state.history_service.borrow();
2021-12-01 03:48:05 +00:00
state.historic_routes.last().unwrap()
2021-11-03 04:35:56 +00:00
},
2021-11-22 20:22:42 +00:00
)
2021-11-03 04:35:56 +00:00
}
2021-12-15 02:46:19 +00:00
pub fn use_router_service<R: Routable>(cx: &ScopeState) -> Option<&Rc<RouterService<R>>> {
2021-11-11 21:36:51 +00:00
cx.use_hook(|_| cx.consume_state::<RouterService<R>>(), |f| f.as_ref())
2021-11-03 04:35:56 +00:00
}
#[derive(Props)]
2021-12-15 02:46:19 +00:00
pub struct LinkProps<'a, R: Routable> {
2021-11-03 04:35:56 +00:00
to: R,
2021-11-22 20:22:42 +00:00
/// The url that gets pushed to the history stack
///
/// You can either put it your own inline method or just autoderive the route using `derive(Routable)`
///
/// ```rust
///
/// Link { to: Route::Home, href: |_| "home".to_string() }
///
/// // or
///
/// Link { to: Route::Home, href: Route::as_url }
///
/// ```
href: fn(&R) -> String,
#[builder(default)]
2021-12-15 02:46:19 +00:00
children: Element<'a>,
2021-11-03 04:35:56 +00:00
}
2021-12-15 02:46:19 +00:00
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
let service = use_router_service::<R>(&cx)?;
2021-11-03 04:35:56 +00:00
cx.render(rsx! {
a {
2021-12-15 02:46:19 +00:00
href: format_args!("{}", (cx.props.href)(&cx.props.to)),
onclick: move |_| service.push_route(cx.props.to.clone()),
// todo!() {&cx.props.children},
2021-11-03 04:35:56 +00:00
}
})
}