Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support query parameters in routes #752

Merged
merged 13 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion examples/router/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use sycamore::prelude::*;
use sycamore_router::{HistoryIntegration, Route, Router};
use sycamore_router::{create_query, HistoryIntegration, Route, Router};

#[derive(Route, Clone)]
enum AppRoutes {
Expand All @@ -11,6 +11,8 @@ enum AppRoutes {
Wildcard { path: Vec<String> },
#[to("/uint-capture/<unit>")]
Unit(u32),
#[to("/query-params")]
QueryParams,
#[not_found]
NotFound,
}
Expand All @@ -32,6 +34,8 @@ fn App() -> View {
br {}
a(href="/uint-capture/42") {"Unit: 42"}
br {}
a(href="/query-params") {"Query Params"}
br {}
a(href="/not-found") {"Not Found"}
br {}

Expand All @@ -51,6 +55,14 @@ fn App() -> View {
AppRoutes::Unit(unit) => view! {
h1 { "Unit: " (unit) }
},
AppRoutes::QueryParams => {
let q = create_query("q");
view! {
h1 { "Query Params" }
a(href="?q=a") { "A" } a(href="?q=b") { "B" }
p { "Query: " (q.get_clone().unwrap_or_default()) }
}
}
AppRoutes::NotFound => view! {
h1 { "Not Found" }
},
Expand Down
1 change: 1 addition & 0 deletions packages/sycamore-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ features = [
"PopStateEvent",
"Url",
"Window",
"UrlSearchParams",
]
version = "0.3.60"
108 changes: 83 additions & 25 deletions packages/sycamore-router/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use sycamore::prelude::*;
use wasm_bindgen::prelude::*;
use web_sys::{Element, HtmlAnchorElement, HtmlBaseElement, KeyboardEvent};
use web_sys::{Element, Event, HtmlAnchorElement, HtmlBaseElement, KeyboardEvent, UrlSearchParams};

use crate::Route;

Expand All @@ -24,6 +24,7 @@

thread_local! {
static PATHNAME: Cell<Option<Signal<String>>> = const { Cell::new(None) };
static QUERY: Cell<Option<Signal<()>>> = const { Cell::new(None) };
}

/// A router integration that uses the
Expand Down Expand Up @@ -78,6 +79,7 @@
let origin = a.origin();
let a_pathname = a.pathname();
let hash = a.hash();
let query = a.search();

Check warning on line 82 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L82

Added line #L82 was not covered by tests

let meta_keys_pressed = meta_keys_pressed(ev.unchecked_ref::<KeyboardEvent>());
if !meta_keys_pressed && location.origin() == Ok(origin) {
Expand All @@ -98,8 +100,34 @@
.unwrap_throw();
window().scroll_to_with_x_and_y(0.0, 0.0);
lukechu10 marked this conversation as resolved.
Show resolved Hide resolved
});
} else if location.search().as_ref() != Ok(&query) {

Check warning on line 103 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L103

Added line #L103 was not covered by tests
// Same origin, same pathname, different query.
ev.prevent_default();
let history = window().history().unwrap_throw();
if query.is_empty() {
history
.push_state_with_url(&JsValue::UNDEFINED, "", Some(&a.href()))
.unwrap_throw();
} else {
let history = window().history().unwrap_throw();
history
.push_state_with_url(&JsValue::UNDEFINED, "", Some(&query))
.unwrap_throw();
}
QUERY.with(|query| query.get().unwrap_throw().update(|_| {}));

Check warning on line 117 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L105-L117

Added lines #L105 - L117 were not covered by tests
} else if location.hash().as_ref() != Ok(&hash) {
// Same origin, same pathname, different hash. Use default browser behavior.
// Same origin, same pathname, same query, different hash. Use default
// browser behavior.
if hash.is_empty() {
ev.prevent_default();
let history = window().history().unwrap_throw();
history
.push_state_with_url(&JsValue::UNDEFINED, "", Some(&a.href()))
.unwrap_throw();
window()
.dispatch_event(&Event::new("hashchanged").unwrap())
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
.unwrap_throw();
}

Check warning on line 130 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L121-L130

Added lines #L121 - L130 were not covered by tests
} else {
// Same page. Do nothing.
ev.prevent_default();
Expand Down Expand Up @@ -233,6 +261,7 @@
let path = integration.current_pathname();
let path = path.strip_prefix(&base_pathname).unwrap_or(&path);
pathname.set(Some(create_signal(path.to_string())));
QUERY.set(Some(create_signal(())));

Check warning on line 264 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L264

Added line #L264 was not covered by tests
});
let pathname = PATHNAME.with(|p| p.get().unwrap_throw());

Expand Down Expand Up @@ -370,44 +399,73 @@
});
}

/// Navigates to the specified `url` without touching the history API.
///
/// This means that the url will not be updated and will continue to show the previous value.
///
/// # Panics
/// This function will `panic!()` if a [`Router`] has not yet been created.
pub fn navigate_no_history(url: &str) {
/// Creates a ReadSignal that tracks the url query provided.
pub fn create_query(query: &'static str) -> ReadSignal<Option<String>> {

Check warning on line 403 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L403

Added line #L403 was not covered by tests
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
PATHNAME.with(|pathname| {
assert!(
pathname.get().is_some(),
"navigate_no_history can only be used with a Router"
"create_query can only be used with a Router"

Check warning on line 407 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L407

Added line #L407 was not covered by tests
);

let pathname = pathname.get().unwrap_throw();
let path = url.strip_prefix(&base_pathname()).unwrap_or(url);
pathname.set(path.to_string());

window().scroll_to_with_x_and_y(0.0, 0.0);
});
create_memo(move || {
QUERY.with(|query| query.get().unwrap_throw()).track();
pathname.track();
UrlSearchParams::new_with_str(&window().location().search().unwrap_throw())
.unwrap_throw()
.get(query)
})
})

Check warning on line 419 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L412-L419

Added lines #L412 - L419 were not covered by tests
}

/// Preform a "soft" refresh of the current page.
///
/// Unlike a "hard" refresh which corresponds to clicking on the refresh button, this simply forces a re-render of the view for the current page.
///
/// # Panic
/// This function will `panic!()` if a [`Router`] has not yet been created.
pub fn refresh() {
/// Creates a ReadSignal that tracks the url query string.
pub fn create_queries() -> ReadSignal<UrlSearchParams> {

Check warning on line 423 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L423

Added line #L423 was not covered by tests
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
PATHNAME.with(|pathname| {
assert!(
pathname.get().is_some(),
"refresh can only be used with a Router"
"create_queries can only be used with a Router"

Check warning on line 427 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L427

Added line #L427 was not covered by tests
);

pathname.get().unwrap_throw().update(|_| {});
let pathname = pathname.get().unwrap_throw();

Check warning on line 430 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L430

Added line #L430 was not covered by tests

window().scroll_to_with_x_and_y(0.0, 0.0);
});
create_memo(move || {
QUERY.with(|query| query.get().unwrap_throw()).track();
pathname.track();
UrlSearchParams::new_with_str(&window().location().search().unwrap_throw())
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
.unwrap_throw()
})
})
}

Check warning on line 439 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L432-L439

Added lines #L432 - L439 were not covered by tests

/// Creates a ReadSignal that tracks the url fragment.
pub fn create_fragment() -> ReadSignal<String> {
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
PATHNAME.with(|pathname| {
assert!(
pathname.get().is_some(),
"create_fragment can only be used with a Router"

Check warning on line 446 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L442-L446

Added lines #L442 - L446 were not covered by tests
);

let pathname = pathname.get().unwrap_throw();

let on_frag_change = create_signal(());
davidon-top marked this conversation as resolved.
Show resolved Hide resolved
window()
.add_event_listener_with_callback(
"hashchange",
Closure::wrap(Box::new(move || {
on_frag_change.update(|_| {});
}) as Box<dyn FnMut()>)
.into_js_value()
.unchecked_ref(),
)
.unwrap_throw();

create_memo(move || {
on_frag_change.track();
pathname.track();
window().location().hash().unwrap_throw()
})
})

Check warning on line 468 in packages/sycamore-router/src/router.rs

View check run for this annotation

Codecov / codecov/patch

packages/sycamore-router/src/router.rs#L449-L468

Added lines #L449 - L468 were not covered by tests
}

fn meta_keys_pressed(kb_event: &KeyboardEvent) -> bool {
Expand Down
Loading