@@ -14,18 +14,26 @@ import {
1414 input ,
1515 OnInit ,
1616 Provider ,
17+ signal ,
1718 TemplateRef ,
1819 viewChild ,
1920 viewChildren ,
2021 ViewEncapsulation
2122} from '@angular/core' ;
23+ import { takeUntilDestroyed , toObservable } from '@angular/core/rxjs-interop' ;
2224import { RouterLink } from '@angular/router' ;
2325import { KbqButton , KbqButtonModule , KbqButtonStyles } from '@koobiq/components/button' ;
2426import { KbqComponentColors , KbqDefaultSizes , PopUpPlacements } from '@koobiq/components/core' ;
2527import { KbqDropdownModule } from '@koobiq/components/dropdown' ;
2628import { KbqIconModule } from '@koobiq/components/icon' ;
27- import { KbqOverflowItem , KbqOverflowItemsModule } from '@koobiq/components/overflow-items' ;
29+ import {
30+ ElementVisibilityManager ,
31+ KbqOverflowItem ,
32+ KbqOverflowItemsModule ,
33+ KbqOverflowItemsResult
34+ } from '@koobiq/components/overflow-items' ;
2835import { KbqTitleModule } from '@koobiq/components/title' ;
36+ import { debounceTime , merge } from 'rxjs' ;
2937import { KbqBreadcrumbsConfiguration , KbqBreadcrumbsWrapMode } from './breadcrumbs.types' ;
3038import { RdxRovingFocusGroupDirective } from './roving-focus-group.directive' ;
3139import { RdxRovingFocusItemDirective } from './roving-focus-item.directive' ;
@@ -195,11 +203,12 @@ export class KbqBreadcrumbs {
195203
196204 protected readonly separator = contentChild ( KbqBreadcrumbsSeparator , { read : TemplateRef } ) ;
197205
198- protected readonly items = contentChildren ( forwardRef ( ( ) => KbqBreadcrumbItem ) ) ;
206+ protected readonly items = contentChildren < KbqBreadcrumbItem > ( forwardRef ( ( ) => KbqBreadcrumbItem ) ) ;
199207
200208 private readonly breadcrumbsResult = viewChild ( 'breadcrumbsResult' , { read : ElementRef } ) ;
209+ private readonly result = viewChild ( KbqOverflowItemsResult ) ;
201210
202- private readonly overflowItems = viewChildren ( forwardRef ( ( ) => KbqOverflowItem ) ) ;
211+ private readonly overflowItems = viewChildren < KbqOverflowItem > ( forwardRef ( ( ) => KbqOverflowItem ) ) ;
203212
204213 /**
205214 * Ensures at least minimum number of breadcrumb items are shown.
@@ -217,35 +226,20 @@ export class KbqBreadcrumbs {
217226 * @returns {number | null } The computed max width for overflow items or null if conditions are not met.
218227 * @docs -private
219228 */
220- protected readonly maxWidth = computed ( ( ) : number | null => {
221- const overflowItems = this . overflowItems ( ) ;
222- const max = this . max ( ) ;
223-
224- if ( ! overflowItems . length || max === null || max >= overflowItems . length || max < this . minVisibleItems ) {
225- return null ;
226- }
227-
228- // Reorders overflow items to prioritize the first and last elements
229- const sortedItems = [
230- ...overflowItems . slice ( 1 , - 1 ) ,
231- overflowItems [ 0 ] ,
232- overflowItems [ overflowItems . length - 1 ]
233- ] ;
234-
235- let visibleItemsWidth = 0 ;
236-
237- for ( let i = 0 ; i < max ; i ++ ) {
238- visibleItemsWidth += this . getItemWidth ( sortedItems [ sortedItems . length - i - 1 ] ) ;
239- }
240-
241- return visibleItemsWidth ;
242- } ) ;
229+ protected readonly maxWidth = signal < number | null > ( null ) ;
243230
244231 constructor ( ) {
245232 const group = inject ( RdxRovingFocusGroupDirective , { self : true } ) ;
246233
247234 group . orientation = 'horizontal' ;
248235
236+ // Defer measurement to after layout (same pattern as KbqOverflowItems.setupObservers).
237+ // computed() fires synchronously during change detection before the browser has finished
238+ // layout, so offsetWidth reads return incorrect values at that point.
239+ merge ( toObservable ( this . overflowItems ) , toObservable ( this . max ) )
240+ . pipe ( debounceTime ( 0 ) , takeUntilDestroyed ( ) )
241+ . subscribe ( ( ) => this . maxWidth . set ( this . calculateMaxWidth ( ) ) ) ;
242+
249243 effect ( ( ) => {
250244 const focusableItems = group . focusableItems ( ) ;
251245 const expandButton = this . breadcrumbsResult ( ) ?. nativeElement ;
@@ -258,7 +252,31 @@ export class KbqBreadcrumbs {
258252 } ) ;
259253 }
260254
261- private getItemWidth ( item ?: KbqOverflowItem ) {
255+ private calculateMaxWidth ( ) : number | null {
256+ const overflowItems = this . overflowItems ( ) ;
257+ const max = this . max ( ) ;
258+
259+ if ( ! overflowItems . length || max === null || max >= overflowItems . length || max < this . minVisibleItems ) {
260+ return null ;
261+ }
262+
263+ // Reorders overflow items to prioritize the last and first elements
264+ const sortedItems = [
265+ ...overflowItems . slice ( 1 , - 1 ) ,
266+ overflowItems [ 0 ] ,
267+ overflowItems [ overflowItems . length - 1 ]
268+ ] ;
269+
270+ let width = this . getItemWidth ( this . result ( ) ) ?? 0 ;
271+
272+ for ( let i = 0 ; i < max - 1 ; i ++ ) {
273+ width += this . getItemWidth ( sortedItems [ sortedItems . length - i - 1 ] ) ;
274+ }
275+
276+ return width ;
277+ }
278+
279+ private getItemWidth ( item ?: ElementVisibilityManager ) {
262280 return item ? item . element . offsetWidth : 0 ;
263281 }
264282}
0 commit comments