@@ -8,14 +8,17 @@ export default function useItemsOverflow({
88 containerRef,
99 gap,
1010 deductedSpaceRef,
11- itemRefs
11+ itemRefs,
12+ minVisibleCount = 0
1213} : {
1314 containerRef : RefObject < HTMLElement > ;
1415 gap : number ;
1516 deductedSpaceRef ?: RefObject < HTMLElement > ;
1617 itemRefs : RefObject < HTMLElement > [ ] ;
18+ minVisibleCount ?: number ;
1719} ) {
18- const [ visibleCount , setVisibleCount ] = useState < number > ( itemRefs . length ) ;
20+ const [ visibleCount , setVisibleCount ] = useState < number > ( 0 ) ;
21+ const [ hasMeasured , setHasMeasured ] = useState < boolean > ( false ) ;
1922 const itemWidthsRef = useRef < number [ ] > ( [ ] ) ;
2023 const deductedWidthRef = useRef < number > ( 0 ) ;
2124 const isCalculatingRef = useRef ( false ) ;
@@ -47,8 +50,11 @@ export default function useItemsOverflow({
4750 break ;
4851 }
4952 }
50- setVisibleCount ( count ) ;
51- } , [ containerRef , itemRefs , gap ] ) ;
53+
54+ // Ensure at least minVisibleCount items are visible
55+ const finalCount = Math . max ( count , Math . min ( minVisibleCount , maxIter ) ) ;
56+ setVisibleCount ( finalCount ) ;
57+ } , [ containerRef , itemRefs , gap , minVisibleCount ] ) ;
5258
5359 const measureDeductedWidth = useCallback ( ( ) => {
5460 if ( deductedSpaceRef ?. current ) {
@@ -58,35 +64,42 @@ export default function useItemsOverflow({
5864 }
5965 } , [ deductedSpaceRef ] ) ;
6066
67+ const measureAndCacheItemsSync = useCallback ( ( ) => {
68+ const container = containerRef . current ;
69+ if ( ! container || ! itemRefs . length ) {
70+ setVisibleCount ( itemRefs . length ) ;
71+ setHasMeasured ( true ) ;
72+ return ;
73+ }
74+
75+ measureDeductedWidth ( ) ;
76+
77+ const itemElements = itemRefs . map ( ref => ref . current ) . filter ( el => el !== null ) as HTMLElement [ ] ;
78+
79+ if ( itemElements . length === 0 ) {
80+ setVisibleCount ( 0 ) ;
81+ itemWidthsRef . current = [ ] ;
82+ setHasMeasured ( true ) ;
83+ return ;
84+ }
85+
86+ itemWidthsRef . current = itemElements . map ( item => item . getBoundingClientRect ( ) . width ) ;
87+ calculateFromCachedWidths ( ) ;
88+ setHasMeasured ( true ) ;
89+ } , [ containerRef , itemRefs , calculateFromCachedWidths , measureDeductedWidth ] ) ;
90+
6191 const measureAndCacheItems = useCallback ( ( ) => {
6292 if ( isCalculatingRef . current ) return ;
6393 isCalculatingRef . current = true ;
6494
6595 requestAnimationFrame ( ( ) => {
6696 try {
67- const container = containerRef . current ;
68- if ( ! container || ! itemRefs . length ) {
69- setVisibleCount ( itemRefs . length ) ;
70- return ;
71- }
72-
73- measureDeductedWidth ( ) ;
74-
75- const itemElements = itemRefs . map ( ref => ref . current ) . filter ( el => el !== null ) as HTMLElement [ ] ;
76-
77- if ( itemElements . length === 0 ) {
78- setVisibleCount ( 0 ) ;
79- itemWidthsRef . current = [ ] ;
80- return ;
81- }
82-
83- itemWidthsRef . current = itemElements . map ( item => item . getBoundingClientRect ( ) . width ) ;
84- calculateFromCachedWidths ( ) ;
97+ measureAndCacheItemsSync ( ) ;
8598 } finally {
8699 isCalculatingRef . current = false ;
87100 }
88101 } ) ;
89- } , [ containerRef , itemRefs , calculateFromCachedWidths , measureDeductedWidth ] ) ;
102+ } , [ measureAndCacheItemsSync ] ) ;
90103
91104 useIsomorphicLayoutEffect ( ( ) => {
92105 if ( ! containerRef . current ) return ;
@@ -116,12 +129,15 @@ export default function useItemsOverflow({
116129
117130 useIsomorphicLayoutEffect ( ( ) => {
118131 if ( itemRefs . length > 0 ) {
119- measureAndCacheItems ( ) ;
132+ setHasMeasured ( false ) ;
133+ // Use synchronous measurement for initial render to prevent delay
134+ measureAndCacheItemsSync ( ) ;
120135 } else {
121136 setVisibleCount ( 0 ) ;
122137 itemWidthsRef . current = [ ] ;
138+ setHasMeasured ( true ) ;
123139 }
124- } , [ itemRefs , measureAndCacheItems ] ) ;
140+ } , [ itemRefs , measureAndCacheItemsSync ] ) ;
125141
126- return visibleCount ;
142+ return { visibleCount, hasMeasured } ;
127143}
0 commit comments