wip: docs and router
This commit is contained in:
1 parent
8bf57dc21d
commit
a5f05d73ac
50 files changed
+1263
-234
No files matched your search
@@ -37,3 +37,9 @@ default = ["web"]
|
||||
web = ["web-sys"]
|
||||
desktop = []
|
||||
mobile = []
|
||||
|
||||
[dev-dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
dioxus-web = { path = "../web" }
|
||||
log = "0.4.14"
|
||||
wasm-logger = "0.2.0"
|
||||
@@ -0,0 +1,40 @@
|
||||
# Router hook for Dioxus apps
|
||||
|
||||
Dioxus-router provides a use_router hook that returns a different value depending on the route.
|
||||
The router is generic over any value, however it makes sense to return a different set of VNodes
|
||||
and feed them into the App's return VNodes.
|
||||
|
||||
Using the router should feel similar to tide's routing framework where an "address" book is assembled at the head of the app.
|
||||
|
||||
Here's an example of how to use the router hook:
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Routable)]
|
||||
enum AppRoute {
|
||||
Home,
|
||||
Posts,
|
||||
NotFound
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
let route = use_router(cx, AppRoute::parse);
|
||||
|
||||
match route {
|
||||
AppRoute::Home => rsx!(cx, Home {})
|
||||
AppRoute::Posts => rsx!(cx, Posts {})
|
||||
AppRoute::Notfound => rsx!(cx, Notfound {})
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Adding links into your app:
|
||||
|
||||
```rust
|
||||
static Leaf: FC<()> = |cx, props| {
|
||||
rsx!(cx, div {
|
||||
Link { to: AppRoute::Home }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Currently, the router is only supported in a web environment, but we plan to add 1st-party support via the context API when new renderers are available.
|
||||
@@ -0,0 +1,45 @@
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
use dioxus_router::*;
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
dioxus_web::launch(App, |c| c);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum Route {
|
||||
Home,
|
||||
About,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props| {
|
||||
let route = use_router(cx, Route::parse);
|
||||
|
||||
match route {
|
||||
Route::Home => rsx!(cx, div { "Home" }),
|
||||
Route::About => rsx!(cx, div { "About" }),
|
||||
Route::NotFound => rsx!(cx, div { "NotFound" }),
|
||||
}
|
||||
};
|
||||
|
||||
impl ToString for Route {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Route::Home => "/".to_string(),
|
||||
Route::About => "/about".to_string(),
|
||||
Route::NotFound => "/404".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Route {
|
||||
fn parse(s: &str) -> Self {
|
||||
match s {
|
||||
"/" => Route::Home,
|
||||
"/about" => Route::About,
|
||||
_ => Route::NotFound,
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-23
@@ -11,14 +11,18 @@ use web_sys::Event;
|
||||
|
||||
use crate::utils::fetch_base_url;
|
||||
|
||||
pub trait Routable: 'static + Send + Clone + ToString + PartialEq {}
|
||||
impl<T> Routable for T where T: 'static + Send + Clone + ToString + PartialEq {}
|
||||
|
||||
pub struct RouterService<R: Routable> {
|
||||
history: RefCell<Vec<R>>,
|
||||
historic_routes: RefCell<Vec<R>>,
|
||||
history_service: web_sys::History,
|
||||
base_ur: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
impl<R: Routable> RouterService<R> {
|
||||
fn push_route(&self, r: R) {
|
||||
self.history.borrow_mut().push(r);
|
||||
self.historic_routes.borrow_mut().push(r);
|
||||
}
|
||||
|
||||
fn get_current_route(&self) -> &str {
|
||||
@@ -61,7 +65,7 @@ impl<R: Routable> RouterService<R> {
|
||||
/// 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
|
||||
pub fn use_router<R: Routable>(cx: Context, cfg: impl FnOnce(&str) -> R) -> Option<&R> {
|
||||
pub fn use_router<R: Routable>(cx: Context, parse: impl FnMut(&str) -> R) -> &R {
|
||||
// for the web, attach to the history api
|
||||
cx.use_hook(
|
||||
|f| {
|
||||
@@ -71,10 +75,13 @@ pub fn use_router<R: Routable>(cx: Context, cfg: impl FnOnce(&str) -> R) -> Opti
|
||||
let base_url = fetch_base_url();
|
||||
|
||||
let service: RouterService<R> = RouterService {
|
||||
history: RefCell::new(vec![]),
|
||||
historic_routes: RefCell::new(vec![]),
|
||||
history_service: web_sys::window().unwrap().history().expect("no history"),
|
||||
base_ur: RefCell::new(base_url),
|
||||
};
|
||||
|
||||
// service.history_service.push_state(data, title);
|
||||
|
||||
cx.provide_state(service);
|
||||
|
||||
let regenerate = cx.schedule_update();
|
||||
@@ -105,30 +112,13 @@ pub struct LinkProps<R: Routable> {
|
||||
children: Element,
|
||||
}
|
||||
|
||||
pub fn Link<'a, R: Routable>(cx: Context, props: &LinkProps<R>) -> Element {
|
||||
pub fn Link<R: Routable>(cx: Context, props: &LinkProps<R>) -> Element {
|
||||
let service = use_router_service::<R>(cx)?;
|
||||
cx.render(rsx! {
|
||||
a {
|
||||
href: format_args!("{}", props.to.to_path()),
|
||||
href: format_args!("{}", props.to.to_string()),
|
||||
onclick: move |_| service.push_route(props.to.clone()),
|
||||
{&props.children},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub trait Routable: Sized + Clone + 'static {
|
||||
/// Converts path to an instance of the routes enum.
|
||||
fn from_path(path: &str, params: &HashMap<&str, &str>) -> Option<Self>;
|
||||
|
||||
/// Converts the route to a string that can passed to the history API.
|
||||
fn to_path(&self) -> String;
|
||||
|
||||
/// Lists all the available routes
|
||||
fn routes() -> Vec<&'static str>;
|
||||
|
||||
/// The route to redirect to on 404
|
||||
fn not_found_route() -> Option<Self>;
|
||||
|
||||
/// Match a route based on the path
|
||||
fn recognize(pathname: &str) -> Option<Self>;
|
||||
}
|
||||
Reference in new issue
Block a user