yew_nested_router/components/
link.rs1use crate::prelude::{use_router, Target};
2use crate::state::State;
3use gloo_events::{EventListener, EventListenerOptions};
4use web_sys::HtmlElement;
5use yew::prelude::*;
6
7#[derive(Clone, Debug, PartialEq, Properties)]
9pub struct LinkProperties<T>
10where
11 T: Target,
12{
13 #[prop_or_default]
15 pub children: Html,
16
17 #[prop_or_default]
18 pub id: Option<AttrValue>,
19
20 pub to: T,
22
23 #[prop_or_default]
25 pub state: State,
26
27 #[prop_or_default]
28 pub any: bool,
29
30 #[prop_or_default]
31 pub predicate: Option<Callback<T, bool>>,
32
33 #[prop_or_else(default::element)]
35 pub element: String,
36
37 #[prop_or_default]
39 pub suppress_href: bool,
40
41 #[prop_or_default]
43 pub suppress_hash: bool,
44
45 #[prop_or_default]
47 pub class: Classes,
48
49 #[prop_or_default]
51 pub active: Classes,
52
53 #[prop_or_default]
55 pub inactive: Classes,
56}
57
58mod default {
59 pub fn element() -> String {
60 "a".to_string()
61 }
62}
63
64#[function_component(Link)]
66pub fn link<T>(props: &LinkProperties<T>) -> Html
67where
68 T: Target + 'static,
69{
70 let router = use_router::<T>().expect("Need Router or Nested component");
71
72 let mut class = props.class.clone();
73
74 let active = match props.any {
75 true => {
76 let active = router.active();
77 active.is_some()
78 }
79 false => match &props.predicate {
80 Some(predicate) => router
81 .active()
82 .clone()
83 .map(|t| predicate.emit(t))
84 .unwrap_or(false),
85 None => router.is_same(&props.to),
86 },
87 };
88
89 match active {
90 true => class.extend(props.active.clone()),
91 false => class.extend(props.inactive.clone()),
92 }
93
94 let href = match props.element.as_str() {
95 "a" if !props.suppress_href => {
96 Some(router.render_target_with(props.to.clone(), props.state.clone()))
97 }
98 _ => None,
99 };
100
101 let node_ref = use_node_ref();
102
103 use_effect_with(
104 (
105 router,
106 props.to.clone(),
107 props.state.clone(),
108 node_ref.clone(),
109 ),
110 |(router, to, state, node_ref)| {
111 let mut listener = None;
112
113 if let Some(element) = node_ref.cast::<HtmlElement>() {
114 let router = router.clone();
115 let to = to.clone();
116 let state = state.clone();
117 listener = Some(EventListener::new_with_options(
118 &element,
119 "click",
120 EventListenerOptions::enable_prevent_default(),
121 move |e| {
122 e.prevent_default();
123 router.push_with(to.clone(), state.clone());
124 },
125 ));
126 }
127
128 move || drop(listener)
129 },
130 );
131
132 html!(
133 <@{props.element.clone()}
134 {class}
135 {href}
136 ref={node_ref}
137 id={props.id.clone()}
138 >
139 { props.children.clone() }
140 </@>
141 )
142}