1use freya_core::prelude::*;
2use freya_router::prelude::{
3 NavigationTarget,
4 RouterContext,
5};
6
7use crate::{
8 define_theme,
9 get_theme,
10 tooltip::{
11 Tooltip,
12 TooltipContainer,
13 },
14};
15
16define_theme! {
17 %[component]
18 pub Link {
19 %[fields]
20 color: Color,
21 }
22}
23
24#[derive(Clone, PartialEq)]
26pub enum LinkTooltip {
27 None,
29 Default,
34 Custom(String),
36}
37
38#[derive(PartialEq)]
39pub struct Link {
40 pub(crate) theme: Option<LinkThemePartial>,
42 to: NavigationTarget,
44 children: Vec<Element>,
46 tooltip: LinkTooltip,
48 key: DiffKey,
50}
51
52impl ChildrenExt for Link {
53 fn get_children(&mut self) -> &mut Vec<Element> {
54 &mut self.children
55 }
56}
57
58impl KeyExt for Link {
59 fn write_key(&mut self) -> &mut DiffKey {
60 &mut self.key
61 }
62}
63
64impl Link {
65 pub fn new(to: impl Into<NavigationTarget>) -> Self {
66 Self {
67 to: to.into(),
68 children: Vec::new(),
69 tooltip: LinkTooltip::Default,
70 theme: None,
71 key: DiffKey::None,
72 }
73 }
74
75 pub fn tooltip(mut self, tooltip: impl Into<LinkTooltip>) -> Self {
76 self.tooltip = tooltip.into();
77 self
78 }
79}
80
81impl Component for Link {
82 fn render(&self) -> impl IntoElement {
83 let theme = get_theme!(&self.theme, LinkThemePreference, "link");
84 let mut is_hovering = use_state(|| false);
85
86 let url = if let NavigationTarget::External(ref url) = self.to {
87 Some(url.clone())
88 } else {
89 None
90 };
91
92 let on_pointer_enter = move |_| {
93 is_hovering.set(true);
94 };
95
96 let on_pointer_leave = move |_| {
97 is_hovering.set(false);
98 };
99
100 let on_press = {
101 let to = self.to.clone();
102 let url = url.clone();
103 move |_| {
104 if let Some(url) = &url {
107 let _ = open::that(url);
108 } else {
109 let _ = RouterContext::get().push(to.clone());
110 }
111 }
112 };
113
114 let color = if *is_hovering.read() {
115 Some(theme.color)
116 } else {
117 None
118 };
119
120 let tooltip_text = match &self.tooltip {
121 LinkTooltip::Default => url,
122 LinkTooltip::None => None,
123 LinkTooltip::Custom(str) => Some(str.clone()),
124 };
125
126 let link = rect()
127 .on_press(on_press)
128 .on_pointer_enter(on_pointer_enter)
129 .on_pointer_leave(on_pointer_leave)
130 .map(color, |rect, color| rect.color(color))
131 .children(self.children.clone());
132
133 if let Some(tooltip_text) = tooltip_text {
134 TooltipContainer::new(Tooltip::new(tooltip_text))
135 .child(link)
136 .into_element()
137 } else {
138 link.into()
139 }
140 }
141
142 fn render_key(&self) -> DiffKey {
143 self.key.clone().or(self.default_key())
144 }
145}