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 pub group: Option<String>,
53}
54
55impl NavigationItem {
56 pub fn at(label: impl Into<String>, url: impl Into<String>) -> Self {
58 Self {
59 label: label.into(),
60 target: NavTarget::Url(url.into()),
61 order: 0,
62 icon: None,
63 group: None,
64 }
65 }
66
67 #[must_use]
69 pub fn icon(mut self, icon: IconData) -> Self {
70 self.icon = Some(icon);
71 self
72 }
73
74 #[must_use]
77 pub fn group(mut self, group: impl Into<String>) -> Self {
78 self.group = Some(group.into());
79 self
80 }
81
82 pub(crate) fn resolved(mut self, url: &str) -> Self {
84 if matches!(self.target, NavTarget::Derived) {
85 self.target = NavTarget::Url(url.to_string());
86 }
87 self
88 }
89
90 pub fn url(&self) -> Option<&str> {
92 self.target.url()
93 }
94
95 pub(crate) fn path(&self) -> Option<&str> {
97 let url = self.url()?;
98 let path = url.split_once(['?', '#']).map_or(url, |(path, _)| path);
99 (!path.is_empty()).then_some(path)
100 }
101
102 pub fn is_current_path(&self, current_path: &str) -> bool {
105 let Some(path) = self.path() else {
106 return false;
107 };
108 if current_path == path {
109 return true;
110 }
111 current_path
112 .strip_prefix(path)
113 .is_some_and(|rest| rest.starts_with('/'))
114 }
115}
116
117#[cfg(test)]
118mod tests;