freya_components/
link.rs

1use freya_core::prelude::*;
2use freya_router::prelude::{
3    NavigationTarget,
4    Navigator,
5};
6
7use crate::{
8    get_theme,
9    theming::component_themes::LinkThemePartial,
10    tooltip::{
11        Tooltip,
12        TooltipContainer,
13    },
14};
15
16/// Tooltip configuration for the [`Link`] component.
17#[derive(Clone, PartialEq)]
18pub enum LinkTooltip {
19    /// No tooltip at all.
20    None,
21    /// Default tooltip.
22    ///
23    /// - For a route, this is the same as [`None`](LinkTooltip::None).
24    /// - For a URL, this is the value of that URL.
25    Default,
26    /// Custom tooltip to always show.
27    Custom(String),
28}
29
30#[derive(PartialEq)]
31pub struct Link {
32    /// Theme override.
33    pub(crate) theme: Option<LinkThemePartial>,
34    /// The route or external URL string to navigate to.
35    to: NavigationTarget,
36    /// Inner children for the Link.
37    children: Vec<Element>,
38    /// A text hint to show when hovering over the link.
39    tooltip: LinkTooltip,
40}
41
42impl ChildrenExt for Link {
43    fn get_children(&mut self) -> &mut Vec<Element> {
44        &mut self.children
45    }
46}
47
48impl Link {
49    pub fn new(to: impl Into<NavigationTarget>) -> Self {
50        Self {
51            to: to.into(),
52            children: Vec::new(),
53            tooltip: LinkTooltip::Default,
54            theme: None,
55        }
56    }
57
58    pub fn tooltip(mut self, tooltip: impl Into<LinkTooltip>) -> Self {
59        self.tooltip = tooltip.into();
60        self
61    }
62}
63
64impl Render for Link {
65    fn render(&self) -> impl IntoElement {
66        let theme = get_theme!(&self.theme, link);
67        let mut is_hovering = use_state(|| false);
68
69        let url = if let NavigationTarget::External(ref url) = self.to {
70            Some(url.clone())
71        } else {
72            None
73        };
74
75        let on_pointer_enter = move |_| {
76            is_hovering.set(true);
77        };
78
79        let on_pointer_leave = move |_| {
80            is_hovering.set(false);
81        };
82
83        let on_press = {
84            let to = self.to.clone();
85            let url = url.clone();
86            move |_| {
87                // Open the url if there is any
88                // otherwise change the freya router route
89                if let Some(url) = &url {
90                    let _ = open::that(url);
91                } else {
92                    Navigator::get().push(to.clone());
93                }
94            }
95        };
96
97        let color = if *is_hovering.read() {
98            Some(theme.color)
99        } else {
100            None
101        };
102
103        let tooltip_text = match &self.tooltip {
104            LinkTooltip::Default => url.clone(),
105            LinkTooltip::None => None,
106            LinkTooltip::Custom(str) => Some(str.clone()),
107        };
108
109        let link = rect()
110            .on_press(on_press)
111            .on_pointer_enter(on_pointer_enter)
112            .on_pointer_leave(on_pointer_leave)
113            .map(color, |rect, color| rect.color(color))
114            .children(self.children.clone());
115
116        if let Some(tooltip_text) = tooltip_text {
117            TooltipContainer::new(Tooltip::new(tooltip_text))
118                .child(link)
119                .into_element()
120        } else {
121            link.into()
122        }
123    }
124}