mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-14 00:17:17 +00:00
make serde optional
This commit is contained in:
parent
fe601e2a48
commit
2f473f7c97
7 changed files with 289 additions and 89 deletions
|
@ -16,17 +16,19 @@ dioxus = { path="../dioxus" }
|
||||||
dioxus-router-macro = { path="../router-macro" }
|
dioxus-router-macro = { path="../router-macro" }
|
||||||
gloo = { version = "0.8.0", optional = true }
|
gloo = { version = "0.8.0", optional = true }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"], optional = true }
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
url = "2.3.1"
|
url = "2.3.1"
|
||||||
wasm-bindgen = { version = "0.2.86", optional = true }
|
wasm-bindgen = { version = "0.2.86", optional = true }
|
||||||
web-sys = { version = "0.3.60", optional = true, features = ["ScrollRestoration"] }
|
web-sys = { version = "0.3.60", optional = true, features = ["ScrollRestoration"] }
|
||||||
gloo-utils = { version = "0.1.6", optional = true, features = ["serde"] }
|
js-sys = { version = "0.3.63", optional = true }
|
||||||
|
gloo-utils = { version = "0.1.6", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["web"]
|
default = ["web"]
|
||||||
wasm_test = []
|
wasm_test = []
|
||||||
web = ["gloo", "web-sys", "wasm-bindgen", "gloo-utils"]
|
serde = ["dep:serde", "gloo-utils/serde"]
|
||||||
|
web = ["gloo", "web-sys", "wasm-bindgen", "gloo-utils", "js-sys"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
dioxus = { path = "../dioxus" }
|
dioxus = { path = "../dioxus" }
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_router::prelude::*;
|
use dioxus_router::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -128,7 +127,7 @@ fn Route3(cx: Scope, dynamic: String) -> Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Routable)]
|
#[derive(Clone, Debug, PartialEq, Routable)]
|
||||||
enum Route {
|
enum Route {
|
||||||
// Nests with parameters have types taken from child routes
|
// Nests with parameters have types taken from child routes
|
||||||
#[nest("/user/:user_id")]
|
#[nest("/user/:user_id")]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use log::error;
|
use log::error;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
use std::{cell::RefCell, str::FromStr};
|
use std::{cell::RefCell, str::FromStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -14,7 +13,7 @@ pub struct RouterCfg<R: Routable> {
|
||||||
config: RefCell<Option<RouterConfiguration<R>>>,
|
config: RefCell<Option<RouterConfiguration<R>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Routable + Serialize + DeserializeOwned> Default for RouterCfg<R>
|
impl<R: Routable> Default for RouterCfg<R>
|
||||||
where
|
where
|
||||||
<R as FromStr>::Err: std::fmt::Display,
|
<R as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -35,7 +34,7 @@ impl<R: Routable> From<RouterConfiguration<R>> for RouterCfg<R> {
|
||||||
|
|
||||||
/// The props for [`GenericRouter`].
|
/// The props for [`GenericRouter`].
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct GenericRouterProps<R: Routable + Serialize + DeserializeOwned>
|
pub struct GenericRouterProps<R: Routable>
|
||||||
where
|
where
|
||||||
<R as FromStr>::Err: std::fmt::Display,
|
<R as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -43,7 +42,7 @@ where
|
||||||
config: RouterCfg<R>,
|
config: RouterCfg<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Routable + Serialize + DeserializeOwned> PartialEq for GenericRouterProps<R>
|
impl<R: Routable> PartialEq for GenericRouterProps<R>
|
||||||
where
|
where
|
||||||
<R as FromStr>::Err: std::fmt::Display,
|
<R as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -54,9 +53,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component that renders the current route.
|
/// A component that renders the current route.
|
||||||
pub fn GenericRouter<R: Routable + Clone + Serialize + DeserializeOwned>(
|
pub fn GenericRouter<R: Routable + Clone>(cx: Scope<GenericRouterProps<R>>) -> Element
|
||||||
cx: Scope<GenericRouterProps<R>>,
|
|
||||||
) -> Element
|
|
||||||
where
|
where
|
||||||
<R as FromStr>::Err: std::fmt::Display,
|
<R as FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use gloo::{console::error, events::EventListener, render::AnimationFrame};
|
use gloo::{console::error, events::EventListener, render::AnimationFrame};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use wasm_bindgen::JsValue;
|
||||||
use web_sys::{window, History, ScrollRestoration, Window};
|
use web_sys::{window, History, ScrollRestoration, Window};
|
||||||
|
|
||||||
use crate::routable::Routable;
|
use crate::routable::Routable;
|
||||||
|
@ -12,7 +12,20 @@ use super::{
|
||||||
HistoryProvider,
|
HistoryProvider,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn update_scroll<R: Serialize + DeserializeOwned + Routable>(window: &Window, history: &History) {
|
#[cfg(not(feature = "serde"))]
|
||||||
|
#[allow(clippy::extra_unused_type_parameters)]
|
||||||
|
fn update_scroll<R>(window: &Window, history: &History) {
|
||||||
|
let scroll = ScrollPosition::of_window(window);
|
||||||
|
if let Err(err) = replace_state_with_url(history, &[scroll.x, scroll.y], None) {
|
||||||
|
error!(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn update_scroll<R: serde::Serialize + serde::de::DeserializeOwned + Routable>(
|
||||||
|
window: &Window,
|
||||||
|
history: &History,
|
||||||
|
) {
|
||||||
if let Some(WebHistoryState { state, .. }) = get_current::<WebHistoryState<R>>(history) {
|
if let Some(WebHistoryState { state, .. }) = get_current::<WebHistoryState<R>>(history) {
|
||||||
let scroll = ScrollPosition::of_window(window);
|
let scroll = ScrollPosition::of_window(window);
|
||||||
let state = WebHistoryState { state, scroll };
|
let state = WebHistoryState { state, scroll };
|
||||||
|
@ -22,7 +35,8 @@ fn update_scroll<R: Serialize + DeserializeOwned + Routable>(window: &Window, hi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[cfg(feature = "serde")]
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
struct WebHistoryState<R> {
|
struct WebHistoryState<R> {
|
||||||
state: R,
|
state: R,
|
||||||
scroll: ScrollPosition,
|
scroll: ScrollPosition,
|
||||||
|
@ -40,7 +54,7 @@ struct WebHistoryState<R> {
|
||||||
///
|
///
|
||||||
/// Application developers are responsible for not rendering the router if the prefix is not present
|
/// Application developers are responsible for not rendering the router if the prefix is not present
|
||||||
/// in the URL. Otherwise, if a router navigation is triggered, the prefix will be added.
|
/// in the URL. Otherwise, if a router navigation is triggered, the prefix will be added.
|
||||||
pub struct WebHistory<R: Serialize + DeserializeOwned + Routable> {
|
pub struct WebHistory<R: Routable> {
|
||||||
do_scroll_restoration: bool,
|
do_scroll_restoration: bool,
|
||||||
history: History,
|
history: History,
|
||||||
listener_navigation: Option<EventListener>,
|
listener_navigation: Option<EventListener>,
|
||||||
|
@ -52,7 +66,8 @@ pub struct WebHistory<R: Serialize + DeserializeOwned + Routable> {
|
||||||
phantom: std::marker::PhantomData<R>,
|
phantom: std::marker::PhantomData<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Serialize + DeserializeOwned + Routable> Default for WebHistory<R>
|
#[cfg(not(feature = "serde"))]
|
||||||
|
impl<R: Routable> Default for WebHistory<R>
|
||||||
where
|
where
|
||||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -61,12 +76,82 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Serialize + DeserializeOwned + Routable> WebHistory<R> {
|
#[cfg(feature = "serde")]
|
||||||
|
impl<R: Routable> Default for WebHistory<R>
|
||||||
|
where
|
||||||
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
R: serde::Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(None, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Routable> WebHistory<R> {
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
/// Create a new [`WebHistory`].
|
/// Create a new [`WebHistory`].
|
||||||
///
|
///
|
||||||
/// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history
|
/// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history
|
||||||
/// state. It'll also set the browsers scroll restoration to `manual`.
|
/// state. It'll also set the browsers scroll restoration to `manual`.
|
||||||
pub fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self
|
fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self
|
||||||
|
where
|
||||||
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
let w = window().expect("access to `window`");
|
||||||
|
let h = w.history().expect("`window` has access to `history`");
|
||||||
|
let document = w.document().expect("`window` has access to `document`");
|
||||||
|
|
||||||
|
let myself = Self::new_inner(
|
||||||
|
prefix,
|
||||||
|
do_scroll_restoration,
|
||||||
|
EventListener::new(&document, "scroll", move |_| {
|
||||||
|
update_scroll::<R>(&w, &h);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let current_route = myself.current_route();
|
||||||
|
let current_url = current_route.to_string();
|
||||||
|
let state = myself.create_state(current_route);
|
||||||
|
let _ = replace_state_with_url(&myself.history, &state, Some(¤t_url));
|
||||||
|
|
||||||
|
myself
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
/// Create a new [`WebHistory`].
|
||||||
|
///
|
||||||
|
/// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history
|
||||||
|
/// state. It'll also set the browsers scroll restoration to `manual`.
|
||||||
|
fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self
|
||||||
|
where
|
||||||
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
R: serde::Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
let w = window().expect("access to `window`");
|
||||||
|
let h = w.history().expect("`window` has access to `history`");
|
||||||
|
let document = w.document().expect("`window` has access to `document`");
|
||||||
|
|
||||||
|
let myself = Self::new_inner(
|
||||||
|
prefix,
|
||||||
|
do_scroll_restoration,
|
||||||
|
EventListener::new(&document, "scroll", move |_| {
|
||||||
|
update_scroll::<R>(&w, &h);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
let current_route = myself.current_route();
|
||||||
|
let current_url = current_route.to_string();
|
||||||
|
let state = myself.create_state(current_route);
|
||||||
|
let _ = replace_state_with_url(&myself.history, &state, Some(¤t_url));
|
||||||
|
|
||||||
|
myself
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(
|
||||||
|
prefix: Option<String>,
|
||||||
|
do_scroll_restoration: bool,
|
||||||
|
event_listener: EventListener,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -78,18 +163,12 @@ impl<R: Serialize + DeserializeOwned + Routable> WebHistory<R> {
|
||||||
history
|
history
|
||||||
.set_scroll_restoration(ScrollRestoration::Manual)
|
.set_scroll_restoration(ScrollRestoration::Manual)
|
||||||
.expect("`history` can set scroll restoration");
|
.expect("`history` can set scroll restoration");
|
||||||
let w = window.clone();
|
Some(event_listener)
|
||||||
let h = history.clone();
|
|
||||||
let document = w.document().expect("`window` has access to `document`");
|
|
||||||
|
|
||||||
Some(EventListener::new(&document, "scroll", move |_| {
|
|
||||||
update_scroll::<R>(&w, &h);
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
false => None,
|
false => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let myself = Self {
|
Self {
|
||||||
do_scroll_restoration,
|
do_scroll_restoration,
|
||||||
history,
|
history,
|
||||||
listener_navigation: None,
|
listener_navigation: None,
|
||||||
|
@ -98,26 +177,75 @@ impl<R: Serialize + DeserializeOwned + Routable> WebHistory<R> {
|
||||||
prefix,
|
prefix,
|
||||||
window,
|
window,
|
||||||
phantom: Default::default(),
|
phantom: Default::default(),
|
||||||
};
|
}
|
||||||
|
|
||||||
let current_route = myself.current_route();
|
|
||||||
let current_url = current_route.to_string();
|
|
||||||
let state = myself.create_state(current_route);
|
|
||||||
let _ = replace_state_with_url(&myself.history, &state, Some(¤t_url));
|
|
||||||
|
|
||||||
myself
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_state(&self, state: R) -> WebHistoryState<R> {
|
fn scroll_pos(&self) -> ScrollPosition {
|
||||||
let scroll = self
|
self.do_scroll_restoration
|
||||||
.do_scroll_restoration
|
|
||||||
.then(|| ScrollPosition::of_window(&self.window))
|
.then(|| ScrollPosition::of_window(&self.window))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
fn create_state(&self, _state: R) -> [f64; 2] {
|
||||||
|
let scroll = self.scroll_pos();
|
||||||
|
[scroll.x, scroll.y]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn create_state(&self, state: R) -> WebHistoryState<R> {
|
||||||
|
let scroll = self.scroll_pos();
|
||||||
WebHistoryState { state, scroll }
|
WebHistoryState { state, scroll }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Serialize + DeserializeOwned + Routable> HistoryProvider<R> for WebHistory<R>
|
impl<R: Routable> WebHistory<R>
|
||||||
|
where
|
||||||
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
fn route_from_location(&self) -> R {
|
||||||
|
R::from_str(
|
||||||
|
&self
|
||||||
|
.window
|
||||||
|
.location()
|
||||||
|
.pathname()
|
||||||
|
.unwrap_or_else(|_| String::from("/")),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|err| panic!("{}", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn full_path(&self, state: &R) -> String {
|
||||||
|
match &self.prefix {
|
||||||
|
None => format!("{state}"),
|
||||||
|
Some(prefix) => format!("{prefix}{state}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_nav(&self, result: Result<(), JsValue>) {
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
if self.do_scroll_restoration {
|
||||||
|
self.window.scroll_to_with_x_and_y(0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => error!("failed to change state: ", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn navigate_external(&mut self, url: String) -> bool {
|
||||||
|
match self.window.location().set_href(&url) {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to navigate to external url (", url, "): ", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<R: serde::Serialize + serde::de::DeserializeOwned + Routable> HistoryProvider<R>
|
||||||
|
for WebHistory<R>
|
||||||
where
|
where
|
||||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -126,14 +254,7 @@ where
|
||||||
// Try to get the route from the history state
|
// Try to get the route from the history state
|
||||||
Some(route) => route.state,
|
Some(route) => route.state,
|
||||||
// If that fails, get the route from the current URL
|
// If that fails, get the route from the current URL
|
||||||
None => R::from_str(
|
None => self.route_from_location(),
|
||||||
&self
|
|
||||||
.window
|
|
||||||
.location()
|
|
||||||
.pathname()
|
|
||||||
.unwrap_or_else(|_| String::from("/")),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|err| panic!("{}", err)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,23 +275,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&mut self, state: R) {
|
fn push(&mut self, state: R) {
|
||||||
let path = match &self.prefix {
|
let path = self.full_path(&state);
|
||||||
None => format!("{state}"),
|
|
||||||
Some(prefix) => format!("{prefix}{state}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let state = self.create_state(state);
|
let state = self.create_state(state);
|
||||||
|
|
||||||
let nav = push_state_and_url(&self.history, &state, path);
|
self.handle_nav(push_state_and_url(&self.history, &state, path));
|
||||||
|
|
||||||
match nav {
|
|
||||||
Ok(_) => {
|
|
||||||
if self.do_scroll_restoration {
|
|
||||||
self.window.scroll_to_with_x_and_y(0.0, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => error!("failed to push state: ", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace(&mut self, state: R) {
|
fn replace(&mut self, state: R) {
|
||||||
|
@ -181,26 +290,11 @@ where
|
||||||
|
|
||||||
let state = self.create_state(state);
|
let state = self.create_state(state);
|
||||||
|
|
||||||
let nav = replace_state_with_url(&self.history, &state, Some(&path));
|
self.handle_nav(replace_state_with_url(&self.history, &state, Some(&path)));
|
||||||
|
|
||||||
match nav {
|
|
||||||
Ok(_) => {
|
|
||||||
if self.do_scroll_restoration {
|
|
||||||
self.window.scroll_to_with_x_and_y(0.0, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => error!("failed to replace state:", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn external(&mut self, url: String) -> bool {
|
fn external(&mut self, url: String) -> bool {
|
||||||
match self.window.location().set_href(&url) {
|
self.navigate_external(url)
|
||||||
Ok(_) => true,
|
|
||||||
Err(e) => {
|
|
||||||
error!("failed to navigate to external url (", url, "): ", e);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn updater(&mut self, callback: std::sync::Arc<dyn Fn() + Send + Sync>) {
|
fn updater(&mut self, callback: std::sync::Arc<dyn Fn() + Send + Sync>) {
|
||||||
|
@ -220,3 +314,67 @@ where
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
impl<R: Routable> HistoryProvider<R> for WebHistory<R>
|
||||||
|
where
|
||||||
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
|
{
|
||||||
|
fn current_route(&self) -> R {
|
||||||
|
self.route_from_location()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_prefix(&self) -> Option<String> {
|
||||||
|
self.prefix.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn go_back(&mut self) {
|
||||||
|
if let Err(e) = self.history.back() {
|
||||||
|
error!("failed to go back: ", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn go_forward(&mut self) {
|
||||||
|
if let Err(e) = self.history.forward() {
|
||||||
|
error!("failed to go forward: ", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, state: R) {
|
||||||
|
let path = self.full_path(&state);
|
||||||
|
|
||||||
|
let state: [f64; 2] = self.create_state(state);
|
||||||
|
self.handle_nav(push_state_and_url(&self.history, &state, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace(&mut self, state: R) {
|
||||||
|
let path = match &self.prefix {
|
||||||
|
None => format!("{state}"),
|
||||||
|
Some(prefix) => format!("{prefix}{state}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = self.create_state(state);
|
||||||
|
self.handle_nav(replace_state_with_url(&self.history, &state, Some(&path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn external(&mut self, url: String) -> bool {
|
||||||
|
self.navigate_external(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn updater(&mut self, callback: std::sync::Arc<dyn Fn() + Send + Sync>) {
|
||||||
|
let w = self.window.clone();
|
||||||
|
let h = self.history.clone();
|
||||||
|
let s = self.listener_animation_frame.clone();
|
||||||
|
let d = self.do_scroll_restoration;
|
||||||
|
|
||||||
|
self.listener_navigation = Some(EventListener::new(&self.window, "popstate", move |_| {
|
||||||
|
(*callback)();
|
||||||
|
if d {
|
||||||
|
let mut s = s.lock().expect("unpoisoned scroll mutex");
|
||||||
|
if let Some([x, y]) = get_current(&h) {
|
||||||
|
*s = Some(ScrollPosition { x, y }.scroll_to(w.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
use gloo::console::error;
|
use gloo::console::error;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use gloo_utils::format::JsValueSerdeExt;
|
use gloo_utils::format::JsValueSerdeExt;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
use web_sys::History;
|
use web_sys::History;
|
||||||
|
|
||||||
pub(crate) fn replace_state_with_url<V: Serialize>(
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub(crate) fn replace_state_with_url(
|
||||||
|
history: &History,
|
||||||
|
value: &[f64; 2],
|
||||||
|
url: Option<&str>,
|
||||||
|
) -> Result<(), JsValue> {
|
||||||
|
let position = js_sys::Array::new();
|
||||||
|
position.push(&JsValue::from(value[0]));
|
||||||
|
position.push(&JsValue::from(value[1]));
|
||||||
|
|
||||||
|
history.replace_state_with_url(&position, "", url)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn replace_state_with_url<V: serde::Serialize>(
|
||||||
history: &History,
|
history: &History,
|
||||||
value: &V,
|
value: &V,
|
||||||
url: Option<&str>,
|
url: Option<&str>,
|
||||||
|
@ -14,7 +28,21 @@ pub(crate) fn replace_state_with_url<V: Serialize>(
|
||||||
history.replace_state_with_url(&position, "", url)
|
history.replace_state_with_url(&position, "", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn push_state_and_url<V: Serialize>(
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub(crate) fn push_state_and_url(
|
||||||
|
history: &History,
|
||||||
|
value: &[f64; 2],
|
||||||
|
url: String,
|
||||||
|
) -> Result<(), JsValue> {
|
||||||
|
let position = js_sys::Array::new();
|
||||||
|
position.push(&JsValue::from(value[0]));
|
||||||
|
position.push(&JsValue::from(value[1]));
|
||||||
|
|
||||||
|
history.push_state_with_url(&position, "", Some(&url))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn push_state_and_url<V: serde::Serialize>(
|
||||||
history: &History,
|
history: &History,
|
||||||
value: &V,
|
value: &V,
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -24,7 +52,8 @@ pub(crate) fn push_state_and_url<V: Serialize>(
|
||||||
history.push_state_with_url(&position, "", Some(&url))
|
history.push_state_with_url(&position, "", Some(&url))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_current<V: DeserializeOwned>(history: &History) -> Option<V> {
|
#[cfg(feature = "serde")]
|
||||||
|
pub(crate) fn get_current<V: serde::de::DeserializeOwned>(history: &History) -> Option<V> {
|
||||||
let state = history.state();
|
let state = history.state();
|
||||||
if let Err(err) = &state {
|
if let Err(err) = &state {
|
||||||
error!(err);
|
error!(err);
|
||||||
|
@ -37,3 +66,19 @@ pub(crate) fn get_current<V: DeserializeOwned>(history: &History) -> Option<V> {
|
||||||
deserialized.ok()
|
deserialized.ok()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serde"))]
|
||||||
|
pub(crate) fn get_current(history: &History) -> Option<[f64; 2]> {
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
let state = history.state();
|
||||||
|
if let Err(err) = &state {
|
||||||
|
error!(err);
|
||||||
|
}
|
||||||
|
state.ok().and_then(|state| {
|
||||||
|
let state = state.dyn_into::<js_sys::Array>().ok()?;
|
||||||
|
let x = state.get(0).as_f64()?;
|
||||||
|
let y = state.get(1).as_f64()?;
|
||||||
|
Some([x, y])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use gloo::render::{request_animation_frame, AnimationFrame};
|
use gloo::render::{request_animation_frame, AnimationFrame};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use web_sys::Window;
|
use web_sys::Window;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub(crate) struct ScrollPosition {
|
pub(crate) struct ScrollPosition {
|
||||||
x: f64,
|
pub x: f64,
|
||||||
y: f64,
|
pub y: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollPosition {
|
impl ScrollPosition {
|
||||||
|
|
|
@ -2,7 +2,6 @@ use crate::contexts::router::RoutingCallback;
|
||||||
use crate::history::HistoryProvider;
|
use crate::history::HistoryProvider;
|
||||||
use crate::routable::Routable;
|
use crate::routable::Routable;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -52,7 +51,7 @@ pub struct RouterConfiguration<R: Routable> {
|
||||||
pub on_update: Option<RoutingCallback<R>>,
|
pub on_update: Option<RoutingCallback<R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Routable + Clone + Serialize + DeserializeOwned> Default for RouterConfiguration<R>
|
impl<R: Routable + Clone> Default for RouterConfiguration<R>
|
||||||
where
|
where
|
||||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue