yew_scroll/lib.rs
1use gloo::events::EventListener;
2use gloo::utils::window;
3use yew::prelude::*;
4
5/// Constant defining default Tailwind CSS classes for the scroll-to-top button.
6const SCROLL_TO_TOP_CLASSES: &'static str =
7 "fixed bottom-4 right-4 bg-blue-500 text-white p-3 rounded-full cursor-pointer hover:bg-blue-600 transition duration-300 ease-in-out";
8
9#[derive(Properties, Clone, PartialEq)]
10pub struct ScrollToTopProps {
11 /// Custom CSS classes for styling the scroll-to-top button.
12 #[prop_or(SCROLL_TO_TOP_CLASSES)]
13 pub css: &'static str,
14
15 /// The vertical offset value (Y position) to show the button.
16 #[prop_or(500.0)]
17 pub top_offset: f64,
18
19 /// Custom SVG content for the scroll-to-top button.
20 #[prop_or_else(default_svg)]
21 pub svg_content: Html,
22}
23
24/// scroll_to_top
25/// A Yew component that provides a button to scroll to the top of the page when clicked.
26///
27/// # Arguments
28/// * `props` - The properties of the component.
29/// - `css` - Custom CSS classes for styling the scroll-to-top button. Defaults to predefined Tailwind classes.
30/// - `top_offset` - The vertical offset value (Y position) to show the button. Defaults to 500.0 pixels.
31/// - `svg_content` - Custom SVG content for the scroll-to-top button. Defaults to a default arrow SVG.
32///
33/// # Returns
34/// (Html): An HTML representation of the scroll-to-top button.
35///
36/// # Examples
37/// ```
38/// // Example of using the scroll_to_top component
39/// use yew::prelude::*;
40/// use yew_scroll::{ScrollToTop, ScrollToTopProps};
41///
42/// // Custom SVG content for the scroll-to-top button (an arrow).
43/// fn custom_svg() -> Html {
44/// html! {
45/// <svg
46/// class="w-6 h-6"
47/// fill="none"
48/// stroke="currentColor"
49/// viewBox="0 0 24 24"
50/// xmlns="http://www.w3.org/2000/svg"
51/// >
52/// <path
53/// stroke-linecap="round"
54/// stroke-linejoin="round"
55/// stroke-width="2"
56/// d="M5 10l7-7m0 0l7 7m-7-7v18"
57/// />
58/// </svg>
59/// }
60/// }
61///
62/// #[function_component(MyComponent)]
63/// pub fn my_component() -> Html {
64/// // Set props for the scroll_to_top component
65/// let scroll_to_top_props = ScrollToTopProps {
66/// css: "custom-css", // Add any custom CSS classes
67/// top_offset: 0.0, // Set the desired top offset value to show the button
68/// svg_content: custom_svg(), // Provide custom SVG content
69/// };
70///
71/// // Render the scroll_to_top component with the specified props
72/// html! {
73/// <>
74/// // Other content in your component
75/// <p>{"Scroll down to see the button"}</p>
76///
77/// // Use the scroll_to_top component
78/// <ScrollToTop ..scroll_to_top_props />
79///
80/// // Default Usage
81/// <ScrollToTop />
82/// </>
83/// }
84/// }
85/// ```
86#[function_component(ScrollToTop)]
87pub fn scroll_to_top(props: &ScrollToTopProps) -> Html {
88 // State handle to track the visibility of the scroll-to-top button.
89 let visible_handle = use_state(|| false);
90 let visible = *visible_handle;
91 let top_offset = props.top_offset.clone();
92
93 // Effect to add a scroll event listener and update the visibility state.
94 use_effect(move || {
95 let listener = EventListener::new(&window(), "scroll", move |_| {
96 let scroll_position = window().scroll_y().unwrap_or_default();
97 visible_handle.set(scroll_position > top_offset);
98 });
99
100 // Cleanup when the component is unmounted.
101 move || {
102 drop(listener);
103 }
104 });
105
106 // Callback for the button click event to scroll to the top.
107 let on_click = Callback::from(|_| {
108 let win = window();
109 win.scroll_to_with_x_and_y(0.0, 0.0);
110 });
111
112 html! {
113 if visible {
114 <div class={SCROLL_TO_TOP_CLASSES} onclick={on_click}>{ props.svg_content.clone() }</div>
115 }
116 }
117}
118
119/// Default SVG content for the scroll-to-top button (an arrow).
120fn default_svg() -> Html {
121 html! {
122 <svg
123 class="w-6 h-6"
124 fill="none"
125 stroke="currentColor"
126 viewBox="0 0 24 24"
127 xmlns="http://www.w3.org/2000/svg"
128 >
129 <path
130 stroke-linecap="round"
131 stroke-linejoin="round"
132 stroke-width="2"
133 d="M5 10l7-7m0 0l7 7m-7-7v18"
134 />
135 </svg>
136 }
137}