yew_bs/components/
scrollspy.rs1use yew::prelude::*;
2use web_sys::Element;
3use wasm_bindgen::JsValue;
4use crate::interop::BsScrollSpy;
5#[derive(Properties, PartialEq)]
6pub struct ScrollSpyProps {
7 #[prop_or_default]
8 pub children: Children,
9 #[prop_or_default]
10 pub target: Option<AttrValue>,
11 #[prop_or(10)]
12 pub offset: i32,
13 #[prop_or(ScrollMethod::Auto)]
14 pub method: ScrollMethod,
15 #[prop_or_default]
16 pub nav_selector: Option<AttrValue>,
17 #[prop_or_default]
18 pub class: Option<AttrValue>,
19 #[prop_or_default]
20 pub node_ref: NodeRef,
21}
22#[derive(Clone, Copy, PartialEq, Debug)]
23pub enum ScrollMethod {
24 Auto,
25 Offset,
26 Position,
27}
28impl ScrollMethod {
29 pub fn as_str(&self) -> &'static str {
30 match self {
31 ScrollMethod::Auto => "auto",
32 ScrollMethod::Offset => "offset",
33 ScrollMethod::Position => "position",
34 }
35 }
36}
37#[function_component(ScrollSpy)]
39pub fn scrollspy(props: &ScrollSpyProps) -> Html {
40 let node_ref = props.node_ref.clone();
41 let options = {
42 let opts = js_sys::Object::new();
43 if let Some(target) = &props.target {
44 js_sys::Reflect::set(
45 &opts,
46 &"target".into(),
47 &JsValue::from(target.as_str()),
48 )
49 .unwrap();
50 }
51 js_sys::Reflect::set(&opts, &"offset".into(), &JsValue::from(props.offset))
52 .unwrap();
53 js_sys::Reflect::set(
54 &opts,
55 &"method".into(),
56 &JsValue::from(props.method.as_str()),
57 )
58 .unwrap();
59 JsValue::from(opts)
60 };
61 {
62 let node_ref = node_ref.clone();
63 use_effect_with(
64 (options.clone(), props.target.clone()),
65 move |_| {
66 if let Some(element) = node_ref.cast::<Element>() {
67 let _bs_scrollspy = BsScrollSpy::new(&element, Some(&options));
68 }
69 || ()
70 },
71 );
72 }
73 let children = props.children.clone();
74 html! {
75 < div ref = { props.node_ref.clone() } class = { props.class.clone() } data - bs
76 - spy = "scroll" data - bs - target = { props.target.clone() } data - bs - offset
77 = { props.offset.to_string() } data - bs - method = { props.method.as_str() } > {
78 for children.iter() } </ div >
79 }
80}
81#[derive(Properties, PartialEq)]
82pub struct ScrollSpyNavProps {
83 #[prop_or_default]
84 pub children: Children,
85 #[prop_or_default]
86 pub class: Option<AttrValue>,
87 #[prop_or_default]
88 pub variant: Option<ScrollSpyNavVariant>,
89 #[prop_or_default]
90 pub node_ref: NodeRef,
91}
92#[derive(Clone, Copy, PartialEq, Debug)]
93pub enum ScrollSpyNavVariant {
94 Pills,
95 Tabs,
96 Underline,
97}
98impl ScrollSpyNavVariant {
99 pub fn as_str(&self) -> &'static str {
100 match self {
101 ScrollSpyNavVariant::Pills => "nav-pills",
102 ScrollSpyNavVariant::Tabs => "nav-tabs",
103 ScrollSpyNavVariant::Underline => "nav-underline",
104 }
105 }
106}
107#[function_component(ScrollSpyNav)]
109pub fn scrollspy_nav(props: &ScrollSpyNavProps) -> Html {
110 let mut classes = Classes::new();
111 classes.push("nav");
112 classes.push("nav-scrollspy");
113 if let Some(variant) = &props.variant {
114 classes.push(variant.as_str());
115 }
116 if let Some(class) = &props.class {
117 classes.push(class.to_string());
118 }
119 html! {
120 < nav ref = { props.node_ref.clone() } class = { classes } > { for props.children
121 .iter() } </ nav >
122 }
123}
124#[derive(Properties, PartialEq)]
126pub struct ScrollSpyNavItemProps {
127 #[prop_or_default]
129 pub children: Children,
130 pub href: AttrValue,
132 #[prop_or_default]
134 pub active: bool,
135 #[prop_or_default]
137 pub disabled: bool,
138 #[prop_or_default]
140 pub class: Option<AttrValue>,
141 #[prop_or_default]
143 pub node_ref: NodeRef,
144}
145#[function_component(ScrollSpyNavItem)]
147pub fn scrollspy_nav_item(props: &ScrollSpyNavItemProps) -> Html {
148 let mut classes = Classes::new();
149 classes.push("nav-item");
150 if let Some(class) = &props.class {
151 classes.push(class.to_string());
152 }
153 let link_classes = {
154 let mut link_classes = Classes::new();
155 link_classes.push("nav-link");
156 if props.active {
157 link_classes.push("active");
158 }
159 if props.disabled {
160 link_classes.push("disabled");
161 }
162 link_classes
163 };
164 let href = format!("#{}", props.href.as_str());
165 html! {
166 < li class = { classes } > < a ref = { props.node_ref.clone() } class = {
167 link_classes } href = { href } > { for props.children.iter() } </ a > </ li >
168 }
169}
170#[derive(Properties, PartialEq)]
172pub struct ScrollSpyContentProps {
173 #[prop_or_default]
175 pub children: Children,
176 #[prop_or_default]
178 pub class: Option<AttrValue>,
179 #[prop_or_default]
181 pub node_ref: NodeRef,
182}
183#[function_component(ScrollSpyContent)]
185pub fn scrollspy_content(props: &ScrollSpyContentProps) -> Html {
186 let mut classes = Classes::new();
187 if let Some(class) = &props.class {
188 classes.push(class.to_string());
189 }
190 html! {
191 < div ref = { props.node_ref.clone() } class = { classes } > { for props.children
192 .iter() } </ div >
193 }
194}
195#[derive(Properties, PartialEq)]
197pub struct ScrollSpySectionProps {
198 #[prop_or_default]
200 pub children: Children,
201 pub id: AttrValue,
203 #[prop_or_default]
205 pub class: Option<AttrValue>,
206 #[prop_or_default]
208 pub node_ref: NodeRef,
209}
210#[function_component(ScrollSpySection)]
212pub fn scrollspy_section(props: &ScrollSpySectionProps) -> Html {
213 let mut classes = Classes::new();
214 if let Some(class) = &props.class {
215 classes.push(class.to_string());
216 }
217 html! {
218 < section ref = { props.node_ref.clone() } id = { props.id.clone() } class = {
219 classes } > { for props.children.iter() } </ section >
220 }
221}