freya_components/scroll_views/
scroll_bar.rs

1use dioxus::prelude::*;
2use freya_elements::{
3    self as dioxus_elements,
4};
5use freya_hooks::{
6    use_applied_theme,
7    ScrollBarTheme,
8    ScrollBarThemeWith,
9};
10
11/// Properties for the [`ScrollBar`] component.
12#[derive(Props, Clone, PartialEq)]
13pub struct ScrollBarProps {
14    /// Theme override.
15    pub theme: Option<ScrollBarThemeWith>,
16    pub children: Element,
17    #[props(into)]
18    pub size: String,
19    #[props(default = 0., into)]
20    pub offset_x: f32,
21    #[props(default = 0., into)]
22    pub offset_y: f32,
23    pub clicking_scrollbar: bool,
24    #[props(default = false)]
25    pub is_vertical: bool,
26}
27
28#[derive(Clone, Copy, PartialEq, Debug)]
29enum ScrollBarState {
30    Idle,
31    Hovering,
32}
33
34/// Scroll bar used for [`crate::ScrollView`] and [`crate::VirtualScrollView`].
35#[allow(non_snake_case)]
36pub fn ScrollBar(
37    ScrollBarProps {
38        size,
39        clicking_scrollbar,
40        offset_x: inner_offset_x,
41        offset_y: inner_offset_y,
42        theme,
43        children,
44        is_vertical,
45    }: ScrollBarProps,
46) -> Element {
47    let mut status = use_signal(|| ScrollBarState::Idle);
48    let ScrollBarTheme { background, .. } = use_applied_theme!(&theme, scroll_bar);
49
50    let onmouseenter = move |_| status.set(ScrollBarState::Hovering);
51    let onmouseleave = move |_| status.set(ScrollBarState::Idle);
52
53    let (inner_size, opacity) = match *status.read() {
54        _ if clicking_scrollbar => (size.as_str(), 225.),
55        ScrollBarState::Idle => ("5", 0.),
56        ScrollBarState::Hovering => (size.as_str(), 225.),
57    };
58
59    let (offset_x, offset_y, width, height, inner_width, inner_height) = if is_vertical {
60        (
61            size.as_str(),
62            "0",
63            size.as_str(),
64            "fill",
65            inner_size,
66            "auto",
67        )
68    } else {
69        (
70            "0",
71            size.as_str(),
72            "fill",
73            size.as_str(),
74            "auto",
75            inner_size,
76        )
77    };
78
79    rsx!(
80        rect {
81            width,
82            height,
83            layer: "-999",
84            offset_x: "-{offset_x}",
85            offset_y: "-{offset_y}",
86            rect {
87                onmouseenter,
88                onmouseleave,
89                a11y_role: "scroll-bar",
90                width: "fill",
91                height: "fill",
92                background: "{background}",
93                background_opacity: "{opacity}",
94                direction: if is_vertical { "vertical" } else { "horizontal" },
95                cross_align: "end",
96                rect {
97                    width: inner_width,
98                    height: inner_height,
99                    offset_x: "{inner_offset_x}",
100                    offset_y: "{inner_offset_y}",
101                    {children}
102                }
103            }
104        }
105    )
106}