leptos_shadcn_breadcrumb/
default.rs

1use leptos::prelude::*;
2use tailwind_fuse::tw_merge;
3
4const BREADCRUMB_CLASS: &str = "";
5const BREADCRUMB_LIST_CLASS: &str = "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5";
6const BREADCRUMB_ITEM_CLASS: &str = "inline-flex items-center gap-1.5";
7const BREADCRUMB_LINK_CLASS: &str = "transition-colors hover:text-foreground";
8const BREADCRUMB_PAGE_CLASS: &str = "font-normal text-foreground";
9const BREADCRUMB_SEPARATOR_CLASS: &str = "[&>svg]:size-3.5";
10const BREADCRUMB_ELLIPSIS_CLASS: &str = "flex h-9 w-9 items-center justify-center";
11
12#[component]
13pub fn Breadcrumb(
14    #[prop(optional)] class: MaybeProp<String>,
15    children: Children,
16) -> impl IntoView {
17    let merged_class = tw_merge!(&format!("{} {}", 
18        BREADCRUMB_CLASS,
19        class.get().unwrap_or_default()
20    ));
21    
22    view! {
23        <nav 
24            aria-label="breadcrumb" 
25            class={merged_class}
26        >
27            {children()}
28        </nav>
29    }
30}
31
32#[component]
33pub fn BreadcrumbList(
34    #[prop(optional)] class: MaybeProp<String>,
35    children: Children,
36) -> impl IntoView {
37    let merged_class = tw_merge!(&format!("{} {}", 
38        BREADCRUMB_LIST_CLASS,
39        class.get().unwrap_or_default()
40    ));
41    
42    view! {
43        <ol class={merged_class}>
44            {children()}
45        </ol>
46    }
47}
48
49#[component]
50pub fn BreadcrumbItem(
51    #[prop(optional)] class: MaybeProp<String>,
52    children: Children,
53) -> impl IntoView {
54    let merged_class = tw_merge!(&format!("{} {}", 
55        BREADCRUMB_ITEM_CLASS,
56        class.get().unwrap_or_default()
57    ));
58    
59    view! {
60        <li class={merged_class}>
61            {children()}
62        </li>
63    }
64}
65
66#[component]
67pub fn BreadcrumbLink(
68    #[prop(optional)] href: MaybeProp<String>,
69    #[prop(optional)] class: MaybeProp<String>,
70    #[prop(optional)] as_child: MaybeProp<bool>,
71    children: Children,
72) -> impl IntoView {
73    let merged_class = tw_merge!(&format!("{} {}", 
74        BREADCRUMB_LINK_CLASS,
75        class.get().unwrap_or_default()
76    ));
77    
78    let is_as_child = as_child.get().unwrap_or(false);
79    
80    if is_as_child {
81        // When as_child is true, render children directly with class applied
82        children()
83    } else {
84        view! {
85            <a 
86                href={href.get()}
87                class={merged_class}
88            >
89                {children()}
90            </a>
91        }.into_any()
92    }
93}
94
95#[component]
96pub fn BreadcrumbPage(
97    #[prop(optional)] class: MaybeProp<String>,
98    children: Children,
99) -> impl IntoView {
100    let merged_class = tw_merge!(&format!("{} {}", 
101        BREADCRUMB_PAGE_CLASS,
102        class.get().unwrap_or_default()
103    ));
104    
105    view! {
106        <span 
107            role="link"
108            aria-disabled="true"
109            aria-current="page"
110            class={merged_class}
111        >
112            {children()}
113        </span>
114    }
115}
116
117#[component]
118pub fn BreadcrumbSeparator(
119    #[prop(optional)] class: MaybeProp<String>,
120    #[prop(optional)] children: Option<Children>,
121) -> impl IntoView {
122    let merged_class = tw_merge!(&format!("{} {}", 
123        BREADCRUMB_SEPARATOR_CLASS,
124        class.get().unwrap_or_default()
125    ));
126    
127    view! {
128        <li role="presentation" aria-hidden="true" class={merged_class}>
129            {if let Some(children) = children {
130                children().into_any()
131            } else {
132                view! { 
133                    <svg 
134                        width="15" 
135                        height="15" 
136                        viewBox="0 0 15 15" 
137                        fill="none" 
138                        xmlns="http://www.w3.org/2000/svg"
139                    >
140                        <path 
141                            d="m5.5 4.5 3 3-3 3" 
142                            stroke="currentColor" 
143                            stroke-width="1" 
144                            stroke-linecap="round" 
145                            stroke-linejoin="round"
146                        />
147                    </svg> 
148                }.into_any()
149            }}
150        </li>
151    }
152}
153
154#[component]
155pub fn BreadcrumbEllipsis(
156    #[prop(optional)] class: MaybeProp<String>,
157) -> impl IntoView {
158    let merged_class = tw_merge!(&format!("{} {}", 
159        BREADCRUMB_ELLIPSIS_CLASS,
160        class.get().unwrap_or_default()
161    ));
162    
163    view! {
164        <span 
165            role="presentation" 
166            aria-hidden="true"
167            class={merged_class}
168        >
169            <svg 
170                width="15" 
171                height="15" 
172                viewBox="0 0 15 15" 
173                fill="none" 
174                xmlns="http://www.w3.org/2000/svg"
175            >
176                <path 
177                    d="M3 6.5a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM7.5 6.5a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM13.5 8a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0Z" 
178                    fill="currentColor"
179                />
180            </svg>
181            <span class="sr-only">"More"</span>
182        </span>
183    }
184}