1- import { useEffect , useState , type PropsWithChildren } from "react" ;
1+ import {
2+ Children ,
3+ cloneElement ,
4+ isValidElement ,
5+ useEffect ,
6+ useState ,
7+ type PropsWithChildren ,
8+ type ReactElement ,
9+ type ReactNode ,
10+ } from "react" ;
211import { CheckIcon , CopyIcon } from "@phosphor-icons/react" ;
312import { Button } from "../../components/button" ;
413import { SkeletonLine } from "../../components/loader/skeleton-line" ;
@@ -39,7 +48,7 @@ export function breadcrumbsVariants({
3948 size = KUMO_BREADCRUMBS_DEFAULT_VARIANTS . size ,
4049} : KumoBreadcrumbsVariantsProps = { } ) {
4150 return cn (
42- "group mr-4 hidden min-w-0 grow items-center sm:flex " ,
51+ "group mr-4 flex min-w-0 grow items-center overflow-hidden whitespace-nowrap " ,
4352 KUMO_BREADCRUMBS_VARIANTS . size [ size ] . classes ,
4453 ) ;
4554}
@@ -59,10 +68,10 @@ const Link = ({
5968 return (
6069 < LinkComponent
6170 to = { href }
62- className = "flex min-w-0 items-center gap-1 text-kumo-subtle no-underline"
71+ className = "flex min-w-0 max-w-full items-center gap-1 text-kumo-subtle no-underline"
6372 >
6473 { ! ! icon && < span className = "flex shrink-0 items-center" > { icon } </ span > }
65- { children }
74+ < span className = "truncate" > { children } </ span >
6675 </ LinkComponent >
6776 ) ;
6877} ;
@@ -88,18 +97,21 @@ function Current({
8897
8998 return (
9099 < div
91- className = "flex items-center gap-1 truncate font-medium"
100+ className = "flex min-w-0 max-w-full items-center gap-1 font-medium"
92101 aria-current = "page"
93102 >
94103 { icon && < span className = "flex shrink-0 items-center" > { icon } </ span > }
95- { children }
104+ < span className = "truncate" > { children } </ span >
96105 </ div >
97106 ) ;
98107}
99108
100109function Separator ( ) {
101110 return (
102- < span className = "flex items-center text-kumo-inactive" aria-hidden = "true" >
111+ < span
112+ className = "flex shrink-0 items-center text-kumo-inactive"
113+ aria-hidden = "true"
114+ >
103115 < svg width = "24" height = "24" fill = "none" viewBox = "0 0 24 24" >
104116 < path
105117 stroke = "currentColor"
@@ -113,6 +125,14 @@ function Separator() {
113125 ) ;
114126}
115127
128+ function MobileEllipsis ( ) {
129+ return (
130+ < span className = "flex shrink-0 items-center text-kumo-subtle" aria-hidden >
131+ ...
132+ </ span >
133+ ) ;
134+ }
135+
116136function Clipboard ( { text } : { text : string } ) {
117137 const [ isCopied , setIsCopied ] = useState ( false ) ;
118138
@@ -192,16 +212,56 @@ export function Breadcrumb({
192212 size = "base" ,
193213 className,
194214} : BreadcrumbsProps ) {
215+ const childArray = Children . toArray ( children ) ;
216+ const mobileChildren = getMobileBreadcrumbChildren ( childArray ) ;
217+
195218 return (
196219 < nav
197220 className = { cn ( breadcrumbsVariants ( { size } ) , className ) }
198221 aria-label = "breadcrumb"
199222 >
200- { children }
223+ < div className = "contents sm:hidden" > { mobileChildren } </ div >
224+ < div className = "hidden sm:contents" > { childArray } </ div >
201225 </ nav >
202226 ) ;
203227}
204228
229+ function isComponentElement (
230+ child : ReactNode ,
231+ component : unknown ,
232+ ) : child is ReactElement {
233+ return isValidElement ( child ) && child . type === component ;
234+ }
235+
236+ function getMobileBreadcrumbChildren ( children : ReactNode [ ] ) : ReactNode [ ] {
237+ const breadcrumbItems = children . filter (
238+ ( child ) =>
239+ isComponentElement ( child , Link ) || isComponentElement ( child , Current ) ,
240+ ) as ReactElement [ ] ;
241+
242+ if ( breadcrumbItems . length <= 2 ) {
243+ return children ;
244+ }
245+
246+ const [ parentItem , currentItem ] = breadcrumbItems . slice ( - 2 ) ;
247+ const trailingItems : ReactNode [ ] = [
248+ < MobileEllipsis key = "kumo-breadcrumb-mobile-ellipsis" /> ,
249+ < Separator key = "kumo-breadcrumb-mobile-separator-leading" /> ,
250+ cloneElement ( parentItem , { key : "kumo-breadcrumb-mobile-parent" } ) ,
251+ < Separator key = "kumo-breadcrumb-mobile-separator-trailing" /> ,
252+ cloneElement ( currentItem , { key : "kumo-breadcrumb-mobile-current" } ) ,
253+ ] ;
254+
255+ const extras = children . filter (
256+ ( child ) =>
257+ ! isComponentElement ( child , Link ) &&
258+ ! isComponentElement ( child , Current ) &&
259+ ! isComponentElement ( child , Separator ) ,
260+ ) ;
261+
262+ return [ ...trailingItems , ...extras ] ;
263+ }
264+
205265Breadcrumb . Link = Link ;
206266Breadcrumb . Current = Current ;
207267Breadcrumb . Separator = Separator ;
0 commit comments