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}