1- import { Component , ElementRef , HostBinding , Input , ViewChild } from '@angular/core' ;
1+ import { AnimationBuilder } from '@angular/animations' ;
2+ import { AfterViewInit , Component , ElementRef , HostBinding , Input , NgZone , OnDestroy , ViewChild } from '@angular/core' ;
23import { mkenum } from '../../core/utils' ;
34import { IgxTabsBase } from '../tabs.base' ;
45import { IgxTabsDirective } from '../tabs.directive' ;
6+ import ResizeObserver from 'resize-observer-polyfill' ;
57
68export const IgxTabsAlignment = mkenum ( {
79 start : 'start' ,
810 end : 'end' ,
911 center : 'center' ,
1012 justify : 'justify'
1113} ) ;
14+
15+ /** @hidden */
16+ enum TabScrollButtonStyle {
17+ Visible = 'visible' ,
18+ Hidden = 'hidden' ,
19+ NotDisplayed = 'not_displayed'
20+ }
21+
1222export type IgxTabsAlignment = ( typeof IgxTabsAlignment ) [ keyof typeof IgxTabsAlignment ] ;
1323
1424/** @hidden */
@@ -49,7 +59,8 @@ let NEXT_TAB_ID = 0;
4959 templateUrl : 'tabs.component.html' ,
5060 providers : [ { provide : IgxTabsBase , useExisting : IgxTabsComponent } ]
5161} )
52- export class IgxTabsComponent extends IgxTabsDirective {
62+
63+ export class IgxTabsComponent extends IgxTabsDirective implements AfterViewInit , OnDestroy {
5364
5465 /**
5566 * An @Input property which determines the tab alignment. Defaults to `start`.
@@ -82,6 +93,14 @@ export class IgxTabsComponent extends IgxTabsDirective {
8293 @ViewChild ( 'selectedIndicator' )
8394 public selectedIndicator : ElementRef < HTMLElement > ;
8495
96+ /** @hidden */
97+ @ViewChild ( 'leftButton' )
98+ public leftButton : ElementRef < HTMLElement > ;
99+
100+ /** @hidden */
101+ @ViewChild ( 'rightButton' )
102+ public rightButton : ElementRef < HTMLElement > ;
103+
85104 /** @hidden */
86105 @HostBinding ( 'class.igx-tabs' )
87106 public defaultClass = true ;
@@ -93,6 +112,34 @@ export class IgxTabsComponent extends IgxTabsDirective {
93112 protected componentName = 'igx-tabs' ;
94113
95114 private _tabAlignment : string | IgxTabsAlignment = 'start' ;
115+ private _resizeObserver : ResizeObserver ;
116+
117+ constructor ( builder : AnimationBuilder , private ngZone : NgZone ) {
118+ super ( builder ) ;
119+ }
120+
121+
122+ /** @hidden @internal */
123+ public ngAfterViewInit ( ) : void {
124+ super . ngAfterViewInit ( ) ;
125+
126+ this . ngZone . runOutsideAngular ( ( ) => {
127+ this . _resizeObserver = new ResizeObserver ( ( ) => {
128+ this . updateScrollButtons ( ) ;
129+ } ) ;
130+ this . _resizeObserver . observe ( this . headerContainer . nativeElement ) ;
131+ this . _resizeObserver . observe ( this . viewPort . nativeElement ) ;
132+ } ) ;
133+ }
134+
135+ /** @hidden @internal */
136+ public ngOnDestroy ( ) : void {
137+ super . ngOnDestroy ( ) ;
138+
139+ this . ngZone . runOutsideAngular ( ( ) => {
140+ this . _resizeObserver . disconnect ( ) ;
141+ } ) ;
142+ }
96143
97144 /** @hidden */
98145 public scrollLeft ( ) {
@@ -154,6 +201,15 @@ export class IgxTabsComponent extends IgxTabsDirective {
154201 return NEXT_TAB_ID ++ ;
155202 }
156203
204+ /** @hidden */
205+ protected onItemChanges ( ) {
206+ super . onItemChanges ( ) ;
207+
208+ Promise . resolve ( ) . then ( ( ) => {
209+ this . updateScrollButtons ( ) ;
210+ } ) ;
211+ }
212+
157213 private alignSelectedIndicator ( element : HTMLElement , duration = 0.3 ) : void {
158214 if ( this . selectedIndicator ) {
159215 this . selectedIndicator . nativeElement . style . visibility = 'visible' ;
@@ -192,6 +248,74 @@ export class IgxTabsComponent extends IgxTabsDirective {
192248
193249 this . offset = ( scrollRight ) ? element . offsetWidth + element . offsetLeft - viewPortWidth : element . offsetLeft ;
194250 this . itemsContainer . nativeElement . style . transform = `translate(${ - this . offset } px)` ;
251+ this . updateScrollButtons ( ) ;
252+ }
253+
254+ private updateScrollButtons ( ) {
255+ const itemsContainerWidth = this . getTabItemsContainerWidth ( ) ;
256+
257+ const leftButtonStyle = this . resolveLeftScrollButtonStyle ( itemsContainerWidth ) ;
258+ this . setScrollButtonStyle ( this . leftButton . nativeElement , leftButtonStyle ) ;
259+
260+ const rightButtonStyle = this . resolveRightScrollButtonStyle ( itemsContainerWidth ) ;
261+ this . setScrollButtonStyle ( this . rightButton . nativeElement , rightButtonStyle ) ;
262+ }
263+
264+ private setScrollButtonStyle ( button : HTMLElement , buttonStyle : TabScrollButtonStyle ) {
265+ if ( buttonStyle === TabScrollButtonStyle . Visible ) {
266+ button . style . visibility = 'visible' ;
267+ button . style . display = '' ;
268+ } else if ( buttonStyle === TabScrollButtonStyle . Hidden ) {
269+ button . style . visibility = 'hidden' ;
270+ button . style . display = '' ;
271+ } else if ( buttonStyle === TabScrollButtonStyle . NotDisplayed ) {
272+ button . style . display = 'none' ;
273+ }
274+ }
275+ private resolveLeftScrollButtonStyle ( itemsContainerWidth : number ) : TabScrollButtonStyle {
276+ const headerContainerWidth = this . headerContainer . nativeElement . offsetWidth ;
277+ const offset = this . offset ;
278+
279+ if ( offset === 0 ) {
280+ // Fix for IE 11, a difference is accumulated from the widths calculations.
281+ if ( itemsContainerWidth - headerContainerWidth <= 1 ) {
282+ return TabScrollButtonStyle . NotDisplayed ;
283+ }
284+ return TabScrollButtonStyle . Hidden ;
285+ } else {
286+ return TabScrollButtonStyle . Visible ;
287+ }
288+ }
289+
290+ private resolveRightScrollButtonStyle ( itemsContainerWidth : number ) : TabScrollButtonStyle {
291+ const viewPortWidth = this . viewPort . nativeElement . offsetWidth ;
292+ const headerContainerWidth = this . headerContainer . nativeElement . offsetWidth ;
293+ const offset = this . offset ;
294+ const total = offset + viewPortWidth ;
295+
296+ // Fix for IE 11, a difference is accumulated from the widths calculations.
297+ if ( itemsContainerWidth - headerContainerWidth <= 1 && offset === 0 ) {
298+ return TabScrollButtonStyle . NotDisplayed ;
299+ }
300+
301+ if ( itemsContainerWidth > total ) {
302+ return TabScrollButtonStyle . Visible ;
303+ } else {
304+ return TabScrollButtonStyle . Hidden ;
305+ }
306+ }
307+
308+ private getTabItemsContainerWidth ( ) {
309+ // We use this hacky way to get the width of the itemsContainer,
310+ // because there is inconsistency in IE we cannot use offsetWidth or scrollOffset.
311+ const itemsContainerChildrenCount = this . itemsContainer . nativeElement . children . length ;
312+ let itemsContainerWidth = 0 ;
313+ if ( itemsContainerChildrenCount > 1 ) {
314+ const lastTab = this . itemsContainer . nativeElement . children [ itemsContainerChildrenCount - 2 ] as HTMLElement ;
315+ itemsContainerWidth = lastTab . offsetLeft + lastTab . offsetWidth ;
316+ }
317+
318+ return itemsContainerWidth ;
195319 }
196320}
197321
0 commit comments