rust-dominator/src/routing.rs

103 lines
2.2 KiB
Rust
Raw Normal View History

2019-06-20 19:57:47 -04:00
use std::borrow::Cow;
use js_sys::JsString;
use web_sys::{EventTarget, HtmlElement};
use lazy_static::lazy_static;
use futures_signals::signal::{Mutable, ReadOnlyMutable};
use gloo::events::EventListener;
2019-06-20 19:57:47 -04:00
use crate::bindings;
use crate::dom::{Dom, DomBuilder};
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 ?
EventListener::new(&bindings::window(), "popstate", {
let value = value.clone();
move |_| {
change_url(&value);
}
}).forget();
Self {
value,
2019-06-20 19:57:47 -04:00
}
2018-11-01 07:40:31 -04:00
}
}
2019-06-20 19:57:47 -04:00
lazy_static! {
static ref URL: CurrentUrl = 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(&JsString::from(new_url));
change_url(&URL.value);
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| {
dom.event_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 ?
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", {
2019-06-20 19:57:47 -04:00
.attribute("href", &url)
.apply(on_click_go_to_url(url))
2018-11-01 07:40:31 -04:00
.apply(f)
})
}