rust-dominator/src/routing.rs

131 lines
3.0 KiB
Rust
Raw Normal View History

2019-06-20 19:57:47 -04:00
use std::borrow::Cow;
use web_sys::{EventTarget, HtmlElement};
2021-05-28 09:18:14 -04:00
use once_cell::sync::Lazy;
2019-06-20 19:57:47 -04:00
use futures_signals::signal::{Mutable, ReadOnlyMutable};
2019-06-20 19:57:47 -04:00
use crate::bindings;
use crate::dom::{Dom, DomBuilder, EventOptions};
2019-06-21 14:58:39 -04:00
use crate::utils::EventListener;
use crate::events;
2018-11-01 07:40:31 -04:00
// TODO inline ?
2019-06-20 19:57:47 -04:00
fn change_url(mutable: &Mutable<String>) {
2018-11-01 07:40:31 -04:00
let mut lock = mutable.lock_mut();
2019-06-20 19:57:47 -04:00
let new_url = String::from(bindings::current_url());
2018-11-01 07:40:31 -04:00
// TODO helper method for this
// TODO can this be made more efficient ?
2019-06-20 19:57:47 -04:00
if *lock != new_url {
*lock = new_url;
2018-11-01 07:40:31 -04:00
}
}
struct CurrentUrl {
2019-06-20 19:57:47 -04:00
value: Mutable<String>,
2018-11-01 07:40:31 -04:00
}
impl CurrentUrl {
2019-06-20 19:57:47 -04:00
fn new() -> Self {
// TODO can this be made more efficient ?
2019-06-20 19:57:47 -04:00
let value = Mutable::new(String::from(bindings::current_url()));
// TODO clean this up somehow ?
let _ = EventListener::new(bindings::window_event_target(), "popstate", &EventOptions::default(), {
2019-06-20 19:57:47 -04:00
let value = value.clone();
move |_| {
change_url(&value);
}
2019-06-21 14:58:39 -04:00
});
2019-06-20 19:57:47 -04:00
Self {
value,
2019-06-20 19:57:47 -04:00
}
2018-11-01 07:40:31 -04:00
}
}
2021-05-28 09:18:14 -04:00
static URL: Lazy<CurrentUrl> = Lazy::new(|| CurrentUrl::new());
2018-11-01 07:40:31 -04:00
#[inline]
2019-06-20 19:57:47 -04:00
pub fn url() -> ReadOnlyMutable<String> {
URL.value.read_only()
2018-11-01 07:40:31 -04:00
}
// TODO if URL hasn't been created yet, don't create it
#[inline]
pub fn go_to_url(new_url: &str) {
2019-06-20 19:57:47 -04:00
// TODO intern ?
bindings::go_to_url(new_url);
2019-06-20 19:57:47 -04:00
change_url(&URL.value);
2018-11-01 07:40:31 -04:00
}
#[deprecated(since = "0.5.1", note = "Use the on_click_go_to_url macro instead")]
2018-11-01 07:40:31 -04:00
#[inline]
2019-06-20 19:57:47 -04:00
pub fn on_click_go_to_url<A, B>(new_url: A) -> impl FnOnce(DomBuilder<B>) -> DomBuilder<B>
where A: Into<Cow<'static, str>>,
B: AsRef<EventTarget> {
let new_url = new_url.into();
2018-11-01 07:40:31 -04:00
#[inline]
move |dom| {
2021-10-17 04:49:43 -04:00
dom.event_with_options(&EventOptions::preventable(), move |e: events::Click| {
2018-11-01 07:40:31 -04:00
e.prevent_default();
go_to_url(&new_url);
})
}
}
// TODO better type than HtmlElement
2019-06-20 19:57:47 -04:00
// TODO maybe make this a macro ?
#[deprecated(since = "0.5.1", note = "Use the link macro instead")]
#[allow(deprecated)]
2018-11-01 07:40:31 -04:00
#[inline]
2019-06-20 19:57:47 -04:00
pub fn link<A, F>(url: A, f: F) -> Dom
where A: Into<Cow<'static, str>>,
F: FnOnce(DomBuilder<HtmlElement>) -> DomBuilder<HtmlElement> {
let url = url.into();
2018-11-01 07:40:31 -04:00
html!("a", {
2022-04-10 17:57:51 -04:00
.attr("href", &url)
2019-06-20 19:57:47 -04:00
.apply(on_click_go_to_url(url))
2018-11-01 07:40:31 -04:00
.apply(f)
})
}
// TODO test this
#[macro_export]
macro_rules! on_click_go_to_url {
($this:ident, $url:expr) => {{
let url = $url;
2021-10-17 04:49:43 -04:00
$this.event_with_options(&$crate::EventOptions::preventable(), move |e: $crate::events::Click| {
e.prevent_default();
$crate::routing::go_to_url(&url);
})
}};
}
// TODO test this
#[macro_export]
macro_rules! link {
($url:expr, { $($methods:tt)* }) => {{
let url = $url;
$crate::html!("a", {
2022-04-10 17:57:51 -04:00
.attr("href", &url)
.apply(move |dom| $crate::on_click_go_to_url!(dom, url))
$($methods)*
})
}};
}