1use topcoat::{context::Cx, icon::IconData, view::Attributes};
4
5pub(crate) fn runtime_link(cx: &Cx, href: &str) -> Attributes {
7 topcoat::runtime::link_attrs(cx, href.to_string(), topcoat::runtime::prefetch_mode(cx))
8}
9
10#[derive(Clone, Default, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum NavTarget {
14 #[default]
16 Derived,
17 Url(String),
19}
20
21impl NavTarget {
22 pub fn url(&self) -> Option<&str> {
24 match self {
25 Self::Derived => None,
26 Self::Url(url) => Some(url),
27 }
28 }
29}
30
31impl std::fmt::Debug for NavTarget {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 Self::Derived => f.write_str("Derived"),
35 Self::Url(url) => f.debug_tuple("Url").field(url).finish(),
36 }
37 }
38}
39
40#[derive(Clone, Debug, Default)]
42pub struct NavigationItem {
43 pub label: String,
44 pub target: NavTarget,
46 pub order: i32,
48 pub icon: Option<IconData>,
50}
51
52impl NavigationItem {
53 pub fn at(label: impl Into<String>, url: impl Into<String>) -> Self {
55 Self {
56 label: label.into(),
57 target: NavTarget::Url(url.into()),
58 order: 0,
59 icon: None,
60 }
61 }
62
63 #[must_use]
65 pub fn icon(mut self, icon: IconData) -> Self {
66 self.icon = Some(icon);
67 self
68 }
69
70 pub(crate) fn resolved(mut self, url: &str) -> Self {
72 if matches!(self.target, NavTarget::Derived) {
73 self.target = NavTarget::Url(url.to_string());
74 }
75 self
76 }
77
78 pub fn url(&self) -> Option<&str> {
80 self.target.url()
81 }
82
83 pub fn is_current_path(&self, current_path: &str) -> bool {
85 let Some(url) = self.url() else {
86 return false;
87 };
88 if current_path == url {
89 return true;
90 }
91 current_path
92 .strip_prefix(url)
93 .is_some_and(|rest| rest.starts_with('/'))
94 }
95}
96
97#[cfg(test)]
98mod tests;