yew_router/components/
link.rs1use serde::Serialize;
2use wasm_bindgen::UnwrapThrowExt;
3use yew::prelude::*;
4use yew::virtual_dom::AttrValue;
5
6use crate::navigator::NavigatorKind;
7use crate::prelude::*;
8use crate::{utils, Routable};
9
10#[derive(Properties, Clone, PartialEq)]
12pub struct LinkProps<R, Q = (), S = ()>
13where
14 R: Routable,
15 Q: Clone + PartialEq + Serialize,
16 S: Clone + PartialEq,
17{
18 #[prop_or_default]
20 pub classes: Classes,
21 pub to: R,
23 #[prop_or_default]
25 pub query: Option<Q>,
26 #[prop_or_default]
28 pub state: Option<S>,
29 #[prop_or_default]
30 pub disabled: bool,
31 #[prop_or_default]
33 pub anchor_ref: NodeRef,
34 #[prop_or_default]
35 pub children: Html,
36}
37
38#[function_component]
40pub fn Link<R, Q = (), S = ()>(props: &LinkProps<R, Q, S>) -> Html
41where
42 R: Routable + 'static,
43 Q: Clone + PartialEq + Serialize + 'static,
44 S: Clone + PartialEq + 'static,
45{
46 let LinkProps {
47 classes,
48 to,
49 query,
50 state,
51 disabled,
52 anchor_ref,
53 children,
54 } = props.clone();
55
56 let navigator = use_navigator().expect_throw("failed to get navigator");
57
58 let onclick = {
59 let navigator = navigator.clone();
60 let to = to.clone();
61 let query = query.clone();
62 let state = state.clone();
63
64 Callback::from(move |e: MouseEvent| {
65 if e.meta_key() || e.ctrl_key() || e.shift_key() || e.alt_key() {
66 return;
67 }
68 e.prevent_default();
69 match (&state, &query) {
70 (None, None) => {
71 navigator.push(&to);
72 }
73 (Some(state), None) => {
74 navigator.push_with_state(&to, state.clone());
75 }
76 (None, Some(query)) => {
77 navigator
78 .push_with_query(&to, query)
79 .expect_throw("failed push history with query");
80 }
81 (Some(state), Some(query)) => {
82 navigator
83 .push_with_query_and_state(&to, query, state.clone())
84 .expect_throw("failed push history with query and state");
85 }
86 }
87 })
88 };
89
90 let href = {
91 let route_s = to.to_path();
92 let pathname = navigator.prefix_basename(&route_s);
93 let mut path = query
94 .and_then(|query| serde_urlencoded::to_string(query).ok())
95 .and_then(|query| utils::compose_path(&pathname, &query))
96 .unwrap_or_else(|| pathname.into_owned());
97
98 if navigator.kind() == NavigatorKind::Hash {
99 path.insert(0, '#');
100 }
101
102 AttrValue::from(path)
103 };
104
105 html! {
106 <a class={classes}
107 {href}
108 {onclick}
109 {disabled}
110 ref={anchor_ref}
111 >
112 { children }
113 </a>
114 }
115}