set up routing

This commit is contained in:
Greg Johnston 2024-02-24 06:47:11 -05:00
parent a3c3478831
commit c1f4616a31
6 changed files with 196 additions and 0 deletions

View file

@ -18,6 +18,7 @@ members = [
"leptos_reactive",
"leptos_server",
"reactive_graph",
"routing",
"server_fn",
"server_fn_macro",
"server_fn/server_fn_macro_default",
@ -55,6 +56,7 @@ leptos_meta = { path = "./meta", version = "0.6.5" }
next_tuple = { path = "./next_tuple" }
or_poisoned = { path = "./or_poisoned" }
reactive_graph = { path = "./reactive_graph" }
routing = { path = "./routing" }
server_fn = { path = "./server_fn", version = "0.6.5" }
server_fn_macro = { path = "./server_fn_macro", version = "0.6.5" }
server_fn_macro_default = { path = "./server_fn/server_fn_macro_default", version = "0.6" }

46
routing/Cargo.toml Normal file
View file

@ -0,0 +1,46 @@
[package]
name = "router"
edition = "2021"
version.workspace = true
[dependencies]
const_str_slice_concat = { workspace = true }
next_tuple = { workspace = true }
reactive_graph = { workspace = true, optional = true }
#paste = "1"
thiserror = "1"
#tuplestructops = "0.3"
url = "2"
js-sys = { version = "0.3" }
wasm-bindgen = { version = "0.2" }
tracing = { version = "0.1", optional = true }
[dependencies.web-sys]
version = "0.3"
features = [
# History/Routing
"History",
"HtmlAnchorElement",
"Location",
"MouseEvent",
"Url",
# Form
"FormData",
"HtmlButtonElement",
"HtmlFormElement",
"HtmlInputElement",
"SubmitEvent",
"Url",
"UrlSearchParams",
# Fetching in Hydrate Mode
"Headers",
"Request",
"RequestInit",
"RequestMode",
"Response",
"Window",
]
[features]
tracing = ["dep:tracing"]
reactive_graph = ["dep:reactive_graph"]

12
routing/src/lib.rs Normal file
View file

@ -0,0 +1,12 @@
//mod generate_route_list;
pub mod location;
pub mod params;
//pub mod matching;
//cfg(feature = "reaccy")]
//pub mod reactive;
//mod render_mode;
//pub mod route;
//pub mod router;
//mod static_render;
//pub use generate_route_list::*;
//pub use render_mode::*;

View file

@ -0,0 +1,78 @@
use crate::params::Params;
use std::fmt::Debug;
use wasm_bindgen::JsValue;
mod server;
pub use server::*;
pub(crate) const BASE: &str = "https://leptos.dev";
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Url {
pub origin: String,
pub pathname: String,
pub search: String,
pub search_params: Params<String>,
pub hash: String,
}
/// A description of a navigation.
#[derive(Debug, Clone, PartialEq)]
pub struct LocationChange {
/// The new URL.
pub value: String,
/// If true, the new location will replace the current one in the history stack, i.e.,
/// clicking the "back" button will not return to the current location.
pub replace: bool,
/// If true, the router will scroll to the top of the page at the end of the navigation.
pub scroll: bool,
/// The [`state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) that will be added during navigation.
pub state: State,
}
impl Default for LocationChange {
fn default() -> Self {
Self {
value: Default::default(),
replace: true,
scroll: true,
state: Default::default(),
}
}
}
pub trait Location {
type Error: Debug;
/// Sets up any global event listeners or other initialization needed.
fn init(&self);
/// Returns the current URL.
fn try_to_url(&self) -> Result<Url, Self::Error>;
fn set_navigation_hook(&mut self, cb: impl Fn(Url) + 'static);
/// Navigate to a new location.
fn navigate(&self, loc: &LocationChange);
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct State(pub Option<JsValue>);
impl State {
pub fn to_js_value(&self) -> JsValue {
match &self.0 {
Some(v) => v.clone(),
None => JsValue::UNDEFINED,
}
}
}
impl<T> From<T> for State
where
T: Into<JsValue>,
{
fn from(value: T) -> Self {
State(Some(value.into()))
}
}

View file

@ -0,0 +1,57 @@
use super::{Location, LocationChange, Url};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestUrl(String);
impl RequestUrl {
/// Creates a server-side request URL from a path, with an optional initial slash.
pub fn from_path(path: impl AsRef<str>) -> Self {
let path = path.as_ref().trim_start_matches('/');
let mut string = String::with_capacity(path.len());
string.push_str(path);
Self(string)
}
}
impl Default for RequestUrl {
fn default() -> Self {
Self(String::from("/"))
}
}
impl Location for RequestUrl {
type Error = url::ParseError;
fn init(&self) {}
fn try_to_url_with_base(&self, base: &str) -> Result<Url, Self::Error> {
let url = String::with_capacity(self.0.len() + 1 + base);
let url = url::Url::parse(&self.0)?;
let search_params = url
.query_pairs()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
Ok(Url {
origin: url.origin().unicode_serialization(),
pathname: url.path().to_string(),
search: url.query().unwrap_or_default().to_string(),
search_params,
hash: Default::default(),
})
}
fn set_navigation_hook(&mut self, _cb: impl FnMut(Url) + 'static) {}
fn navigate(&self, _loc: &LocationChange) {}
}
#[cfg(test)]
mod tests {
use super::RequestUrl;
use crate::location::Location;
pub fn should_parse_url_without_origin() {
let req = RequestUrl::from_path("/foo/bar");
let url = req.try_to_url().expect("could not parse URL");
}
}

1
routing/src/params.rs Normal file
View file

@ -0,0 +1 @@
pub(crate) type Params<K> = Vec<(K, String)>;