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    /// Key for the component.
41    key: DiffKey,
42}
43
44impl ChildrenExt for Link {
45    fn get_children(&mut self) -> &mut Vec<Element> {
46        &mut self.children
47    }
48}
49
50impl KeyExt for Link {
51    fn write_key(&mut self) -> &mut DiffKey {
52        &mut self.key
53    }
54}
55
56impl Link {
57    pub fn new(to: impl Into<NavigationTarget>) -> Self {
58        Self {
59            to: to.into(),
60            children: Vec::new(),
61            tooltip: LinkTooltip::Default,
62            theme: None,
63            key: DiffKey::None,
64        }
65    }
66
67    pub fn tooltip(mut self, tooltip: impl Into<LinkTooltip>) -> Self {
68        self.tooltip = tooltip.into();
69        self
70    }
71}
72
73impl Component for Link {
74    fn render(&self) -> impl IntoElement {
75        let theme = get_theme!(&self.theme, link);
76        let mut is_hovering = use_state(|| false);
77
78        let url = if let NavigationTarget::External(ref url) = self.to {
79            Some(url.clone())
80        } else {
81            None
82        };
83
84        let on_pointer_enter = move |_| {
85            is_hovering.set(true);
86        };
87
88        let on_pointer_leave = move |_| {
89            is_hovering.set(false);
90        };
91
92        let on_press = {
93            let to = self.to.clone();
94            let url = url.clone();
95            move |_| {
96                // Open the url if there is any
97                // otherwise change the freya router route
98                if let Some(url) = &url {
99                    let _ = open::that(url);
100                } else {
101                    Navigator::get().push(to.clone());
102                }
103            }
104        };
105
106        let color = if *is_hovering.read() {
107            Some(theme.color)
108        } else {
109            None
110        };
111
112        let tooltip_text = match &self.tooltip {
113            LinkTooltip::Default => url.clone(),
114            LinkTooltip::None => None,
115            LinkTooltip::Custom(str) => Some(str.clone()),
116        };
117
118        let link = rect()
119            .on_press(on_press)
120            .on_pointer_enter(on_pointer_enter)
121            .on_pointer_leave(on_pointer_leave)
122            .map(color, |rect, color| rect.color(color))
123            .children(self.children.clone());
124
125        if let Some(tooltip_text) = tooltip_text {
126            TooltipContainer::new(Tooltip::new(tooltip_text))
127                .child(link)
128                .into_element()
129        } else {
130            link.into()
131        }
132    }
133
134    fn render_key(&self) -> DiffKey {
135        self.key.clone().or(self.default_key())
136    }
137}