use std::borrow::Cow;
use dioxus::prelude::*;
use dioxus_router::prelude::{
navigator,
NavigationTarget,
};
use freya_core::platform::MouseButton;
use freya_elements::{
self as dioxus_elements,
events::MouseEvent,
};
use freya_hooks::{
use_applied_theme,
LinkThemeWith,
};
use crate::{
Tooltip,
TooltipContainer,
};
#[derive(Clone, PartialEq)]
pub enum LinkTooltip {
None,
Default,
Custom(String),
}
#[allow(non_snake_case)]
#[component]
pub fn Link(
#[props(optional)]
theme: Option<LinkThemeWith>,
#[props(into)]
to: NavigationTarget,
children: Element,
#[props(optional)]
onerror: Option<EventHandler<()>>,
#[props(optional)]
tooltip: Option<LinkTooltip>,
) -> Element {
let theme = use_applied_theme!(&theme, link);
let mut is_hovering = use_signal(|| false);
let url = if let NavigationTarget::External(ref url) = to {
Some(url.clone())
} else {
None
};
let onmouseenter = move |_: MouseEvent| {
is_hovering.set(true);
};
let onmouseleave = move |_: MouseEvent| {
is_hovering.set(false);
};
let onclick = {
to_owned![url, to];
move |event: MouseEvent| {
if !matches!(event.trigger_button, Some(MouseButton::Left)) {
return;
}
if let Some(url) = &url {
let res = open::that(url);
if let (Err(_), Some(onerror)) = (res, onerror.as_ref()) {
onerror.call(());
}
} else {
let router = navigator();
router.push(to.clone());
}
}
};
let color = if *is_hovering.read() {
theme.highlight_color
} else {
Cow::Borrowed("inherit")
};
let tooltip = match tooltip {
None | Some(LinkTooltip::Default) => url.clone(),
Some(LinkTooltip::None) => None,
Some(LinkTooltip::Custom(str)) => Some(str),
};
let link = rsx! {
rect {
onmouseenter,
onmouseleave,
onclick,
color: "{color}",
{children}
}
};
if let Some(tooltip) = tooltip {
rsx!(
TooltipContainer {
tooltip: rsx!(
Tooltip {
text: tooltip
}
),
{link}
}
)
} else {
link
}
}
#[cfg(test)]
mod test {
use dioxus_router::prelude::{
Outlet,
Routable,
Router,
};
use freya::prelude::*;
use freya_testing::prelude::*;
#[tokio::test]
pub async fn link() {
#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
#[layout(Layout)]
#[route("/")]
Home,
#[route("/somewhere")]
Somewhere,
#[route("/..routes")]
NotFound
}
#[allow(non_snake_case)]
#[component]
fn NotFound() -> Element {
rsx! {
label {
"Not found"
}
}
}
#[allow(non_snake_case)]
#[component]
fn Home() -> Element {
rsx! {
label {
"Home"
}
}
}
#[allow(non_snake_case)]
#[component]
fn Somewhere() -> Element {
rsx! {
label {
"Somewhere"
}
}
}
#[allow(non_snake_case)]
#[component]
fn Layout() -> Element {
rsx!(
Link {
to: Route::Home,
Button {
label { "Home" }
}
}
Link {
to: Route::Somewhere,
Button {
label { "Somewhere" }
}
}
Outlet::<Route> {}
)
}
fn link_app() -> Element {
rsx!(Router::<Route> {})
}
let mut utils = launch_test(link_app);
assert_eq!(utils.root().get(2).get(0).text(), Some("Home"));
utils.click_cursor((10., 55.)).await;
assert_eq!(utils.root().get(2).get(0).text(), Some("Somewhere"));
utils.click_cursor((10., 10.)).await;
assert_eq!(utils.root().get(2).get(0).text(), Some("Home"));
}
}