Skip to content

Commit

Permalink
Update Yew Router as per #2113 (#2118)
Browse files Browse the repository at this point in the history
* Add Redirect Comp.

* Fix router behaviour.

* Fix output.

* Fix pr-flow.

* Remove Redirect.

* Readd 77b46bf.

* Router Context, History and Location.

* Finish Porting.

* Add Switch, Fix example.

* Add state.

* Fix pr-flow.

* Fix pr-flow.

* Fix unstable feature for 1.49.

* Add documentation.

* Error Types & Simplify Implementation.

* Add some tests.

* Update documentation.

* Fix route outside of a Switch.
  • Loading branch information
futursolo authored Nov 11, 2021
1 parent 6edb135 commit f43760e
Show file tree
Hide file tree
Showing 22 changed files with 1,444 additions and 389 deletions.
2 changes: 1 addition & 1 deletion examples/router/src/components/author_card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Component for AuthorCard {
</div>
</div>
<footer class="card-footer">
<Link<Route> classes={classes!("card-footer-item")} route={Route::Author { id: author.seed }}>
<Link<Route> classes={classes!("card-footer-item")} to={Route::Author { id: author.seed }}>
{ "Profile" }
</Link<Route>>
</footer>
Expand Down
4 changes: 2 additions & 2 deletions examples/router/src/components/post_card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ impl Component for PostCard {
</figure>
</div>
<div class="card-content">
<Link<Route> classes={classes!("title", "is-block")} route={Route::Post { id: post.seed }}>
<Link<Route> classes={classes!("title", "is-block")} to={Route::Post { id: post.seed }}>
{ &post.title }
</Link<Route>>
<Link<Route> classes={classes!("subtitle", "is-block")} route={Route::Author { id: post.author.seed }}>
<Link<Route> classes={classes!("subtitle", "is-block")} to={Route::Author { id: post.author.seed }}>
{ &post.author.name }
</Link<Route>>
</div>
Expand Down
12 changes: 6 additions & 6 deletions examples/router/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ impl Component for Model {

fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<>
<BrowserRouter>
{ self.view_nav(ctx.link()) }

<main>
<Router<Route> render={Router::render(switch)} />
<Switch<Route> render={Switch::render(switch)} />
</main>
<footer class="footer">
<div class="content has-text-centered">
Expand All @@ -72,7 +72,7 @@ impl Component for Model {
<a href="https://unsplash.com">{ "Unsplash" }</a>
</div>
</footer>
</>
</BrowserRouter>
}
}
}
Expand All @@ -99,10 +99,10 @@ impl Model {
</div>
<div class={classes!("navbar-menu", active_class)}>
<div class="navbar-start">
<Link<Route> classes={classes!("navbar-item")} route={Route::Home}>
<Link<Route> classes={classes!("navbar-item")} to={Route::Home}>
{ "Home" }
</Link<Route>>
<Link<Route> classes={classes!("navbar-item")} route={Route::Posts}>
<Link<Route> classes={classes!("navbar-item")} to={Route::Posts}>
{ "Posts" }
</Link<Route>>

Expand All @@ -112,7 +112,7 @@ impl Model {
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
<Link<Route> classes={classes!("navbar-item")} route={Route::Authors}>
<Link<Route> classes={classes!("navbar-item")} to={Route::Authors}>
{ "Meet the authors" }
</Link<Route>>
</a>
Expand Down
4 changes: 2 additions & 2 deletions examples/router/src/pages/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Component for Post {
</h1>
<h2 class="subtitle">
{ "by " }
<Link<Route> classes={classes!("has-text-weight-semibold")} route={Route::Author { id: post.meta.author.seed }}>
<Link<Route> classes={classes!("has-text-weight-semibold")} to={Route::Author { id: post.meta.author.seed }}>
{ &post.meta.author.name }
</Link<Route>>
</h2>
Expand Down Expand Up @@ -74,7 +74,7 @@ impl Post {
</figure>
<div class="media-content">
<div class="content">
<Link<Route> classes={classes!("is-size-5")} route={Route::Author { id: quote.author.seed }}>
<Link<Route> classes={classes!("is-size-5")} to={Route::Author { id: quote.author.seed }}>
<strong>{ &quote.author.name }</strong>
</Link<Route>>
<p class="is-family-secondary">
Expand Down
25 changes: 15 additions & 10 deletions examples/router/src/pages/post_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::components::{pagination::Pagination, post_card::PostCard};
use crate::Route;
use serde::{Deserialize, Serialize};
use yew::prelude::*;
use yew_router::prelude::*;

const ITEMS_PER_PAGE: u64 = 10;
const TOTAL_PAGES: u64 = u64::MAX / ITEMS_PER_PAGE;
Expand All @@ -25,23 +26,27 @@ impl Component for PostList {
Self
}

fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::ShowPage(page) => {
yew_router::push_route_with_query(Route::Posts, PageQuery { page }).unwrap();
ctx.link()
.history()
.unwrap()
.push_with_query(Route::Posts, PageQuery { page })
.unwrap();
true
}
}
}

fn view(&self, ctx: &Context<Self>) -> Html {
let page = self.current_page();
let page = self.current_page(ctx);

html! {
<div class="section container">
<h1 class="title">{ "Posts" }</h1>
<h2 class="subtitle">{ "All of our quality writing in one place" }</h2>
{ self.view_posts() }
{ self.view_posts(ctx) }
<Pagination
{page}
total_pages={TOTAL_PAGES}
Expand All @@ -52,8 +57,8 @@ impl Component for PostList {
}
}
impl PostList {
fn view_posts(&self) -> Html {
let start_seed = (self.current_page() - 1) * ITEMS_PER_PAGE;
fn view_posts(&self, ctx: &Context<Self>) -> Html {
let start_seed = (self.current_page(ctx) - 1) * ITEMS_PER_PAGE;
let mut cards = (0..ITEMS_PER_PAGE).map(|seed_offset| {
html! {
<li class="list-item mb-5">
Expand All @@ -77,9 +82,9 @@ impl PostList {
}
}

fn current_page(&self) -> u64 {
yew_router::parse_query::<PageQuery>()
.map(|it| it.page)
.unwrap_or(1)
fn current_page(&self, ctx: &Context<Self>) -> u64 {
let location = ctx.link().location().unwrap();

location.query::<PageQuery>().map(|it| it.page).unwrap_or(1)
}
}
47 changes: 17 additions & 30 deletions packages/yew-router-macro/src/routable_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,23 +189,25 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream {
let from_path = input.build_from_path();
let to_path = input.build_to_path();

let not_found_route = match not_found_route {
let maybe_not_found_route = match not_found_route {
Some(route) => quote! { ::std::option::Option::Some(Self::#route) },
None => quote! { ::std::option::Option::None },
};

let cache_thread_local_ident = Ident::new(
&format!("__{}_ROUTER_CURRENT_ROUTE_CACHE", ident),
ident.span(),
);

quote! {
::std::thread_local! {
#[doc(hidden)]
#[allow(non_upper_case_globals)]
static #cache_thread_local_ident: ::std::cell::RefCell<::std::option::Option<#ident>> = ::std::cell::RefCell::new(::std::option::Option::None);
let maybe_default = match not_found_route {
Some(route) => {
quote! {
impl ::std::default::Default for #ident {
fn default() -> Self {
Self::#route
}
}
}
}
None => TokenStream::new(),
};

quote! {
#[automatically_derived]
impl ::yew_router::Routable for #ident {
#from_path
Expand All @@ -216,32 +218,17 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream {
}

fn not_found_route() -> ::std::option::Option<Self> {
#not_found_route
}

fn current_route() -> ::std::option::Option<Self> {
#cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow()))
#maybe_not_found_route
}

fn recognize(pathname: &str) -> ::std::option::Option<Self> {
::std::thread_local! {
static ROUTER: ::yew_router::__macro::Router = ::yew_router::__macro::build_router::<#ident>();
}
let route = ROUTER.with(|router| ::yew_router::__macro::recognize_with_router(router, pathname));
{
let route = ::std::clone::Clone::clone(&route);
#cache_thread_local_ident.with(move |val| {
*val.borrow_mut() = route;
});
}
route
}

fn cleanup() {
#cache_thread_local_ident.with(move |val| {
*val.borrow_mut() = ::std::option::Option::None;
});
ROUTER.with(|router| ::yew_router::__macro::recognize_with_router(router, pathname))
}
}

#maybe_default
}
}
4 changes: 3 additions & 1 deletion packages/yew-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ yew-router-macro = { path = "../yew-router-macro" }

wasm-bindgen = "0.2"
js-sys = "0.3"
gloo = "0.3"
gloo = { version = "0.3", features = ["futures"] }
route-recognizer = "0.3"
serde = "1"
serde_urlencoded = "0.7"
serde-wasm-bindgen = "0.3.1"
thiserror = "1.0.30"

[dependencies.web-sys]
version = "0.3"
Expand Down
22 changes: 15 additions & 7 deletions packages/yew-router/src/components/link.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
use crate::{service, Routable};
use std::marker::PhantomData;

use wasm_bindgen::UnwrapThrowExt;
use yew::prelude::*;

use crate::history::History;
use crate::scope_ext::RouterScopeExt;
use crate::Routable;

/// Props for [`Link`]
#[derive(Properties, Clone, PartialEq)]
pub struct LinkProps<R: Routable + Clone + PartialEq> {
pub struct LinkProps<R: Routable> {
/// CSS classes to add to the anchor element (optional).
#[prop_or_default]
pub classes: Classes,
/// Route that will be pushed when the anchor is clicked.
pub route: R,
pub to: R,
pub children: Children,
}

/// A wrapper around `<a>` tag to be used with [`Router`](crate::Router)
pub struct Link<R: Routable + Clone + PartialEq + 'static> {
pub struct Link<R: Routable + 'static> {
_data: PhantomData<R>,
}

pub enum Msg {
OnClick,
}

impl<R: Routable + Clone + PartialEq + 'static> Component for Link<R> {
impl<R: Routable + 'static> Component for Link<R> {
type Message = Msg;
type Properties = LinkProps<R>;

Expand All @@ -33,7 +38,10 @@ impl<R: Routable + Clone + PartialEq + 'static> Component for Link<R> {
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::OnClick => {
service::push_route(ctx.props().route.clone());
ctx.link()
.history()
.expect_throw("failed to read history")
.push(ctx.props().to.clone());
false
}
}
Expand All @@ -42,7 +50,7 @@ impl<R: Routable + Clone + PartialEq + 'static> Component for Link<R> {
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
<a class={ctx.props().classes.clone()}
href={ctx.props().route.to_path()}
href={ctx.props().to.to_path()}
onclick={ctx.link().callback(|e: MouseEvent| {
e.prevent_default();
Msg::OnClick
Expand Down
2 changes: 2 additions & 0 deletions packages/yew-router/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! Components to interface with [Router][crate::Router].
mod link;
mod redirect;
pub use link::*;
pub use redirect::*;
31 changes: 31 additions & 0 deletions packages/yew-router/src/components/redirect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use wasm_bindgen::UnwrapThrowExt;
use yew::prelude::*;

use crate::history::History;
use crate::hooks::use_history;
use crate::Routable;

/// Props for [`Redirect`]
#[derive(Properties, Clone, PartialEq)]
pub struct RedirectProps<R: Routable> {
/// Route that will be pushed when the component is rendered.
pub to: R,
}

/// A component that will redirect to specified route when rendered.
#[function_component(Redirect)]
pub fn redirect<R>(props: &RedirectProps<R>) -> Html
where
R: Routable + 'static,
{
let history = use_history().expect_throw("failed to read history.");

let target_route = props.to.clone();
use_effect(move || {
history.push(target_route.clone());

|| {}
});

Html::default()
}
Loading

0 comments on commit f43760e

Please sign in to comment.