33import * as React from 'react' ;
44
55import { useForkRef , useResizeObserver } from '../../hooks' ;
6+ import { useCollapseChildren } from '../../hooks/useCollapseChildren' ;
67import type { PopupPlacement } from '../Popup' ;
78import type { AriaLabelingProps , DOMProps , Key , QAProps } from '../types' ;
89import { filterDOMProps } from '../utils/filterDOMProps' ;
@@ -35,6 +36,7 @@ export const Breadcrumbs = React.forwardRef(function Breadcrumbs(
3536) {
3637 const listRef = React . useRef < HTMLOListElement > ( null ) ;
3738 const containerRef = useForkRef ( ref , listRef ) ;
39+ const menuRef = React . useRef < HTMLLIElement > ( null ) ;
3840 const endContentRef = React . useRef < HTMLLIElement > ( null ) ;
3941
4042 const items : React . ReactElement < any > [ ] = [ ] ;
@@ -47,103 +49,30 @@ export const Breadcrumbs = React.forwardRef(function Breadcrumbs(
4749 }
4850 } ) ;
4951
50- const [ visibleItemsCount , setVisibleItemsCount ] = React . useState ( items . length ) ;
51- const [ calculated , setCalculated ] = React . useState ( false ) ;
52- const recalculate = ( visibleItems : number ) => {
53- const list = listRef . current ;
54- if ( ! list ) {
55- return ;
56- }
57- const listItems = Array . from ( list . children ) as HTMLElement [ ] ;
58- const endElement = endContentRef . current ;
59- if ( endElement ) {
60- listItems . pop ( ) ;
61- }
62- if ( listItems . length === 0 ) {
63- setCalculated ( true ) ;
64- return ;
65- }
66- const containerWidth = list . offsetWidth - ( endElement ?. offsetWidth ?? 0 ) ;
67- let newVisibleItemsCount = 0 ;
68- let calculatedWidth = 0 ;
69- let maxItems = props . maxItems || Infinity ;
70-
71- let rootWidth = 0 ;
72- if ( props . showRoot ) {
73- const item = listItems . shift ( ) ;
74- if ( item ) {
75- rootWidth = item . offsetWidth ;
76- calculatedWidth += rootWidth ;
77- }
78- newVisibleItemsCount ++ ;
79- }
80-
81- const hasMenu = items . length > visibleItems ;
82- if ( hasMenu ) {
83- const item = listItems . shift ( ) ;
84- if ( item ) {
85- calculatedWidth += item . offsetWidth ;
86- }
87- maxItems -- ;
88- }
89-
90- if ( props . showRoot && calculatedWidth >= containerWidth ) {
91- calculatedWidth -= rootWidth ;
92- newVisibleItemsCount -- ;
93- }
94-
95- const lastItem = listItems . pop ( ) ;
96- if ( lastItem ) {
97- calculatedWidth += Math . min ( lastItem . offsetWidth , 200 ) ;
98- if ( calculatedWidth < containerWidth ) {
99- newVisibleItemsCount ++ ;
100- }
101- }
102-
103- for ( let i = listItems . length - 1 ; i >= 0 ; i -- ) {
104- const item = listItems [ i ] ;
105- calculatedWidth += item . offsetWidth ;
106- if ( calculatedWidth >= containerWidth ) {
107- break ;
108- }
109- newVisibleItemsCount ++ ;
110- }
111-
112- newVisibleItemsCount = Math . max ( Math . min ( maxItems , newVisibleItemsCount ) , 1 ) ;
113- if ( newVisibleItemsCount === visibleItemsCount ) {
114- setCalculated ( true ) ;
115- } else {
116- setVisibleItemsCount ( newVisibleItemsCount ) ;
117- }
118- } ;
119-
120- const handleResize = React . useCallback ( ( ) => {
121- setVisibleItemsCount ( items . length ) ;
122- setCalculated ( false ) ;
123- } , [ items . length ] ) ;
124- useResizeObserver ( {
125- ref : listRef ,
126- onResize : handleResize ,
52+ const {
53+ calculated,
54+ recalculate,
55+ visibleCount : visibleItemsCount ,
56+ } = useCollapseChildren ( {
57+ containerRef : listRef ,
58+ preservedRefs : [ menuRef , endContentRef ] ,
59+ direction : 'start' ,
60+ minCount : 1 ,
61+ maxCount : typeof props . maxItems === 'number' ? props . maxItems - 1 : undefined ,
12762 } ) ;
63+
12864 useResizeObserver ( {
12965 ref : props . endContent ? endContentRef : undefined ,
130- onResize : handleResize ,
66+ onResize : recalculate ,
13167 } ) ;
13268
13369 const lastChildren = React . useRef < typeof props . children | null > ( null ) ;
13470 React . useLayoutEffect ( ( ) => {
13571 if ( calculated && props . children !== lastChildren . current ) {
13672 lastChildren . current = props . children ;
137- setVisibleItemsCount ( items . length ) ;
138- setCalculated ( false ) ;
139- }
140- } , [ calculated , items . length , props . children ] ) ;
141-
142- React . useLayoutEffect ( ( ) => {
143- if ( ! calculated ) {
144- recalculate ( visibleItemsCount ) ;
73+ recalculate ( ) ;
14574 }
146- } ) ;
75+ } , [ calculated , recalculate , props . children ] ) ;
14776
14877 let contents = items ;
14978 if ( items . length > visibleItemsCount ) {
@@ -220,6 +149,7 @@ export const Breadcrumbs = React.forwardRef(function Breadcrumbs(
220149 }
221150 return (
222151 < li
152+ ref = { isMenu ? menuRef : undefined }
223153 key = { isMenu ? 'menu' : `item-${ key } ` }
224154 className = { b ( 'item' , { calculating : isCurrent && ! calculated , current : isCurrent } ) }
225155 >
0 commit comments