11import { clsx } from 'clsx'
22import type { To } from 'history'
3- import React , { useState , useRef , useCallback , useEffect , useMemo } from 'react'
4- import type { SxProp } from '../sx'
5- import type { ComponentProps } from '../utils/types'
3+ import React , { useState , useRef , useCallback , useEffect , useMemo , type ForwardedRef } from 'react'
64import classes from './Breadcrumbs.module.css'
7- import type { ForwardRefComponent as PolymorphicForwardRefComponent } from '../utils/polymorphic'
8- import { BoxWithFallback } from '../internal/components/BoxWithFallback'
95import Details from '../Details'
106import { ActionList } from '../ActionList'
117import { IconButton } from '../Button/IconButton'
@@ -16,27 +12,29 @@ import {useOnEscapePress} from '../hooks/useOnEscapePress'
1612import { useOnOutsideClick } from '../hooks/useOnOutsideClick'
1713import { useFeatureFlag } from '../FeatureFlags'
1814
19- export type BreadcrumbsProps = React . PropsWithChildren <
20- {
21- /**
22- * Optional class name for the breadcrumbs container.
23- */
24- className ?: string
25- /**
26- * Controls the overflow behavior of the breadcrumbs.
27- * By default all overflowing crumbs will "wrap" in the given space taking up extra height.
28- * In the "menu" option we'll see the overflowing crumbs as part of a menu like dropdown instead of the root breadcrumb.
29- * In "menu-with-root" we see that instead of the root, the menu button will take the place of the next breadcrumb.
30- */
31- overflow ?: 'wrap' | 'menu' | 'menu-with-root'
32- /**
33- * Controls the visual variant of the breadcrumbs.
34- * By default, the breadcrumbs will have a normal appearance.
35- * In the "spacious" option, the breadcrumbs will have increased padding and a more relaxed layout.
36- */
37- variant ?: 'normal' | 'spacious'
38- } & SxProp
39- >
15+ export type BreadcrumbsProps = React . PropsWithChildren < {
16+ /**
17+ * Optional class name for the breadcrumbs container.
18+ */
19+ className ?: string
20+ /**
21+ * Controls the overflow behavior of the breadcrumbs.
22+ * By default all overflowing crumbs will "wrap" in the given space taking up extra height.
23+ * In the "menu" option we'll see the overflowing crumbs as part of a menu like dropdown instead of the root breadcrumb.
24+ * In "menu-with-root" we see that instead of the root, the menu button will take the place of the next breadcrumb.
25+ */
26+ overflow ?: 'wrap' | 'menu' | 'menu-with-root'
27+ /**
28+ * Controls the visual variant of the breadcrumbs.
29+ * By default, the breadcrumbs will have a normal appearance.
30+ * In the "spacious" option, the breadcrumbs will have increased padding and a more relaxed layout.
31+ */
32+ variant ?: 'normal' | 'spacious'
33+ /**
34+ * Allows passing of CSS custom properties to the breadcrumbs container.
35+ */
36+ style ?: React . CSSProperties
37+ } >
4038
4139const BreadcrumbsList = ( { children} : React . PropsWithChildren ) => {
4240 return < ol className = { classes . BreadcrumbsList } > { children } </ ol >
@@ -146,7 +144,7 @@ const getValidChildren = (children: React.ReactNode) => {
146144 return React . Children . toArray ( children ) . filter ( child => React . isValidElement ( child ) ) as React . ReactElement [ ]
147145}
148146
149- function Breadcrumbs ( { className, children, sx : sxProp , overflow = 'wrap' , variant = 'normal' } : BreadcrumbsProps ) {
147+ function Breadcrumbs ( { className, children, style , overflow = 'wrap' , variant = 'normal' } : BreadcrumbsProps ) {
150148 const overflowMenuEnabled = useFeatureFlag ( 'primer_react_breadcrumbs_overflow_menu' )
151149 const wrappedChildren = React . Children . map ( children , child => < li className = { classes . ItemWrapper } > { child } </ li > )
152150 const containerRef = useRef < HTMLElement > ( null )
@@ -176,10 +174,6 @@ function Breadcrumbs({className, children, sx: sxProp, overflow = 'wrap', varian
176174 const MENU_BUTTON_FALLBACK_WIDTH = 32 // Design system small IconButton
177175 const [ menuButtonWidth , setMenuButtonWidth ] = useState ( MENU_BUTTON_FALLBACK_WIDTH )
178176
179- // if (typeof window !== 'undefined') {
180- // effectiveOverflow = overflow
181- // }
182-
183177 useEffect ( ( ) => {
184178 const listElement = containerRef . current ?. querySelector ( 'ol' )
185179 if (
@@ -333,27 +327,25 @@ function Breadcrumbs({className, children, sx: sxProp, overflow = 'wrap', varian
333327 } , [ overflowMenuEnabled , overflow , menuItems , effectiveHideRoot , measureMenuButton , visibleItems , rootItem , children ] )
334328
335329 return overflowMenuEnabled ? (
336- < BoxWithFallback
337- as = "nav"
330+ < nav
338331 className = { clsx ( className , classes . BreadcrumbsBase ) }
339332 aria-label = "Breadcrumbs"
340- sx = { sxProp }
333+ style = { style }
341334 ref = { containerRef }
342335 data-overflow = { overflow }
343336 data-variant = { variant }
344337 >
345338 < BreadcrumbsList > { finalChildren } </ BreadcrumbsList >
346- </ BoxWithFallback >
339+ </ nav >
347340 ) : (
348- < BoxWithFallback
349- as = "nav"
341+ < nav
350342 className = { clsx ( className , classes . BreadcrumbsBase ) }
351343 aria-label = "Breadcrumbs"
352- sx = { sxProp }
344+ style = { style }
353345 data-variant = { variant }
354346 >
355347 < BreadcrumbsList > { wrappedChildren } </ BreadcrumbsList >
356- </ BoxWithFallback >
348+ </ nav >
357349 )
358350}
359351
@@ -367,31 +359,40 @@ const ItemSeparator = () => {
367359 )
368360}
369361
370- type StyledBreadcrumbsItemProps = {
362+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
363+ type DistributiveOmit < T , TOmitted extends PropertyKey > = T extends any ? Omit < T , TOmitted > : never
364+
365+ type StyledBreadcrumbsItemProps < As extends React . ElementType > = {
366+ as ?: As
371367 to ?: To
372368 selected ?: boolean
373369 className ?: string
374- } & SxProp &
375- React . HTMLAttributes < HTMLAnchorElement > &
376- React . ComponentPropsWithRef < 'a' >
377-
378- const BreadcrumbsItem = React . forwardRef ( ( { selected, className, ...rest } , ref ) => {
370+ style ?: React . CSSProperties
371+ } & DistributiveOmit < React . ComponentPropsWithRef < React . ElementType extends As ? 'a' : As > , 'as' >
372+
373+ function BreadcrumbsItemComponent < As extends React . ElementType > (
374+ props : StyledBreadcrumbsItemProps < As > ,
375+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
376+ ref : ForwardedRef < any > ,
377+ ) {
378+ const { as : Component = 'a' , selected, className, ...rest } = props
379379 return (
380- < BoxWithFallback
381- as = "a"
380+ < Component
382381 className = { clsx ( className , classes . Item , selected && 'selected' ) }
383382 aria-current = { selected ? 'page' : undefined }
384383 ref = { ref }
385384 { ...rest }
386385 />
387386 )
388- } ) as PolymorphicForwardRefComponent < 'a' , StyledBreadcrumbsItemProps >
387+ }
389388
390- Breadcrumbs . displayName = 'Breadcrumbs'
389+ BreadcrumbsItemComponent . displayName = 'Breadcrumbs.Item '
391390
392- BreadcrumbsItem . displayName = 'Breadcrumbs.Item'
391+ const BreadcrumbsItem = React . forwardRef ( BreadcrumbsItemComponent )
392+
393+ Breadcrumbs . displayName = 'Breadcrumbs'
393394
394- export type BreadcrumbsItemProps = ComponentProps < typeof BreadcrumbsItem >
395+ export type BreadcrumbsItemProps < As extends React . ElementType = 'a' > = StyledBreadcrumbsItemProps < As >
395396export default Object . assign ( Breadcrumbs , { Item : BreadcrumbsItem } )
396397
397398/**
@@ -407,4 +408,4 @@ export type BreadcrumbProps = BreadcrumbsProps
407408/**
408409 * @deprecated Use the `BreadcrumbsItemProps` type instead
409410 */
410- export type BreadcrumbItemProps = ComponentProps < typeof BreadcrumbsItem >
411+ export type BreadcrumbItemProps < As extends React . ElementType = 'a' > = BreadcrumbsItemProps < As >
0 commit comments