link.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. use std::sync::Arc;
  2. use crate::{use_route, RouterCore};
  3. use dioxus_core as dioxus;
  4. use dioxus_core::prelude::*;
  5. use dioxus_core_macro::{format_args_f, rsx, Props};
  6. use dioxus_html as dioxus_elements;
  7. /// Props for the [`Link`](struct.Link.html) component.
  8. #[derive(Props)]
  9. pub struct LinkProps<'a> {
  10. /// The route to link to. This can be a relative path, or a full URL.
  11. ///
  12. /// ```rust, ignore
  13. /// // Absolute path
  14. /// Link { to: "/home", "Go Home" }
  15. ///
  16. /// // Relative path
  17. /// Link { to: "../", "Go Up" }
  18. /// ```
  19. pub to: &'a str,
  20. /// Set the class of the inner link ['a'](https://www.w3schools.com/tags/tag_a.asp) element.
  21. ///
  22. /// This can be useful when styling the inner link element.
  23. #[props(default, strip_option)]
  24. pub class: Option<&'a str>,
  25. /// Set the class added to the inner link when the current route is the same as the "to" route.
  26. ///
  27. /// To set all of the active classes inside a Router at the same time use the `active_class`
  28. /// prop on the Router component. If both the Router prop as well as this prop are provided then
  29. /// this one has precedence. By default set to `"active"`.
  30. #[props(default, strip_option)]
  31. pub active_class: Option<&'a str>,
  32. /// Set the ID of the inner link ['a'](https://www.w3schools.com/tags/tag_a.asp) element.
  33. ///
  34. /// This can be useful when styling the inner link element.
  35. #[props(default, strip_option)]
  36. pub id: Option<&'a str>,
  37. /// Set the title of the window after the link is clicked..
  38. #[props(default, strip_option)]
  39. pub title: Option<&'a str>,
  40. /// Autodetect if a link is external or not.
  41. ///
  42. /// This is automatically set to `true` and will use http/https detection
  43. #[props(default = true)]
  44. pub autodetect: bool,
  45. /// Is this link an external link?
  46. #[props(default = false)]
  47. pub external: bool,
  48. /// New tab?
  49. #[props(default = false)]
  50. pub new_tab: bool,
  51. /// Pass children into the `<a>` element
  52. pub children: Element<'a>,
  53. }
  54. /// A component that renders a link to a route.
  55. ///
  56. /// `Link` components are just [`<a>`](https://www.w3schools.com/tags/tag_a.asp) elements
  57. /// that link to different pages *within* your single-page app.
  58. ///
  59. /// If you need to link to a resource outside of your app, then just use a regular
  60. /// `<a>` element directly.
  61. ///
  62. /// # Examples
  63. ///
  64. /// ```rust, ignore
  65. /// fn Header(cx: Scope) -> Element {
  66. /// cx.render(rsx!{
  67. /// Link { to: "/home", "Go Home" }
  68. /// })
  69. /// }
  70. /// ```
  71. pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
  72. let svc = cx.use_hook(|_| cx.consume_context::<Arc<RouterCore>>());
  73. let LinkProps {
  74. to,
  75. class,
  76. id,
  77. title,
  78. autodetect,
  79. external,
  80. new_tab,
  81. children,
  82. active_class,
  83. ..
  84. } = cx.props;
  85. let is_http = to.starts_with("http") || to.starts_with("https");
  86. let outerlink = (*autodetect && is_http) || *external;
  87. let prevent_default = if outerlink { "" } else { "onclick" };
  88. let active_class_name = match active_class {
  89. Some(c) => (*c).into(),
  90. None => {
  91. let active_from_router = match svc {
  92. Some(service) => service.cfg.active_class.clone(),
  93. None => None,
  94. };
  95. active_from_router.unwrap_or("active".into())
  96. }
  97. };
  98. let route = use_route(&cx);
  99. let url = route.url();
  100. let path = url.path();
  101. let active = path == cx.props.to;
  102. let active_class = if active { active_class_name } else { "".into() };
  103. cx.render(rsx! {
  104. a {
  105. href: "{to}",
  106. class: format_args!("{} {}", class.unwrap_or(""), active_class),
  107. id: format_args!("{}", id.unwrap_or("")),
  108. title: format_args!("{}", title.unwrap_or("")),
  109. prevent_default: "{prevent_default}",
  110. target: format_args!("{}", if *new_tab { "_blank" } else { "" }),
  111. onclick: move |_| {
  112. if !outerlink {
  113. if let Some(service) = svc {
  114. service.push_route(to, cx.props.title.map(|f| f.to_string()), None);
  115. } else {
  116. log::error!(
  117. "Attempted to create a Link to {} outside of a Router context",
  118. cx.props.to,
  119. );
  120. }
  121. }
  122. },
  123. children
  124. }
  125. })
  126. }