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 Cursor::set(CursorIcon::Pointer);
94 is_hovering.set(true);
95 };
96
97 let on_pointer_leave = move |_| {
98 Cursor::set(CursorIcon::default());
99 is_hovering.set(false);
100 };
101
102 let on_press = {
103 let to = self.to.clone();
104 let url = url.clone();
105 move |_| {
106 if let Some(url) = &url {
109 let _ = open::that(url);
110 } else {
111 let _ = RouterContext::get().push(to.clone());
112 }
113 }
114 };
115
116 let color = if *is_hovering.read() {
117 Some(theme.color)
118 } else {
119 None
120 };
121
122 let tooltip_text = match &self.tooltip {
123 LinkTooltip::Default => url,
124 LinkTooltip::None => None,
125 LinkTooltip::Custom(str) => Some(str.clone()),
126 };
127
128 let link = rect()
129 .on_press(on_press)
130 .on_pointer_enter(on_pointer_enter)
131 .on_pointer_leave(on_pointer_leave)
132 .map(color, |rect, color| rect.color(color))
133 .children(self.children.clone());
134
135 if let Some(tooltip_text) = tooltip_text {
136 TooltipContainer::new(Tooltip::new(tooltip_text))
137 .child(link)
138 .into_element()
139 } else {
140 link.into()
141 }
142 }
143
144 fn render_key(&self) -> DiffKey {
145 self.key.clone().or(self.default_key())
146 }
147}