2022-10-21 16:06:44 +02:00
import {
AfterViewInit ,
ChangeDetectorRef ,
Component ,
ContentChildren ,
ElementRef , EventEmitter ,
Input , OnDestroy , Output ,
QueryList ,
ViewChild
} from "@angular/core" ;
import { SliderTabComponent } from "./slider-tab.component" ;
import { ActivatedRoute , Router } from "@angular/router" ;
import { Subscription } from "rxjs" ;
import Timeout = NodeJS . Timeout ;
declare var UIkit ;
@Component ( {
selector : 'slider-tabs' ,
template : `
2022-10-24 15:37:05 +02:00
< div # sliderElement class = "uk-position-relative" [ class.uk - slider ] = " position = = = ' horizontal ' "
2022-10-24 11:57:52 +02:00
[ ngClass ] = "customClass" >
2023-02-09 11:09:49 +01:00
< div [ class.uk - slider - container - tabs ] = " position = = = ' horizontal ' " [ class.uk - border - bottom ] = " border & & position = = = ' horizontal ' " >
< ul # tabsElement [ class.uk - flex - nowrap ] = " position = = = ' horizontal ' "
2022-10-21 16:06:44 +02:00
[ class . uk - slider - items ] = "position === 'horizontal'"
[ class . uk - tab - left ] = "position === 'left'" [ class . uk - tab - right ] = "position === 'right'"
2023-02-09 11:09:49 +01:00
[ attr . uk - switcher ] = "type === 'static'?('connect:' + connect):null"
[ ngClass ] = "'uk-flex-' + flexPosition + ' ' + tabsClass" >
2022-10-24 15:37:05 +02:00
< ng - container * ngIf = "type === 'static'" >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of leftTabs" [ ngStyle ] = " " [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " class = "uk-text-capitalize uk-text-truncate uk-display-block" >
2023-07-18 12:20:04 +02:00
< a >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2023-01-27 12:54:40 +01:00
< / li >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of rightTabs; let i=index;" [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " [ ngClass ] = " i = = = 0 ? ' uk - flex - 1 uk - flex uk - flex - right ' : ' ' "
2023-02-15 10:53:25 +01:00
class = "uk-text-capitalize uk-text-truncate uk-display-block" >
2023-07-18 12:20:04 +02:00
< a [ ngClass ] = " tab.customClass " >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2023-02-09 11:09:49 +01:00
< / li >
< / n g - c o n t a i n e r >
< ng - container * ngIf = "type === 'dynamic'" >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of leftTabs; let i=index;" [ class.uk - active ] = " tab.active " [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " >
2023-02-15 10:53:25 +01:00
< a [ routerLink ] = " tab.routerLink " [ queryParams ] = " tab.queryParams " [ ngClass ] = " tab.customClass "
( click ) = "showActive(i)"
2023-07-18 12:20:04 +02:00
class = "uk-text-capitalize uk-text-truncate uk-display-block" >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2023-02-09 11:09:49 +01:00
< / li >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of rightTabs; let i=index;" [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " [ class.uk - active ] = " tab.active "
2023-02-15 10:53:25 +01:00
[ ngClass ] = "i === 0?'uk-flex-1 uk-flex uk-flex-right':''" >
< a [ routerLink ] = " tab.routerLink " [ queryParams ] = " tab.queryParams " [ ngClass ] = " tab.customClass "
( click ) = "showActive(i)"
2023-07-18 12:20:04 +02:00
class = "uk-text-capitalize uk-text-truncate uk-display-block" >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2022-10-24 15:37:05 +02:00
< / li >
< / n g - c o n t a i n e r >
2022-10-21 16:06:44 +02:00
< ng - container * ngIf = "type === 'scrollable'" >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of leftTabs" [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " class = "uk-text-capitalize uk-text-truncate uk-display-block" [ class.uk - active ] = " tab.active " >
2023-07-18 12:20:04 +02:00
< a routerLink = "./" [ fragment ] = " tab.id " queryParamsHandling = "merge" [ ngClass ] = " tab.customClass " >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2023-01-27 12:54:40 +01:00
< / li >
2023-04-05 11:16:40 +02:00
< li * ngFor = "let tab of rightTabs; let i=index;" [ style.max - width ] = " ( position = = = ' horizontal ' ) ? ' 50 % ' : null " class = "uk-text-capitalize uk-text-truncate uk-display-block"
2023-01-27 12:54:40 +01:00
[ ngClass ] = "i === 0?'uk-flex-1 uk-flex uk-flex-right':''"
[ class . uk - active ] = "tab.active" >
2023-07-18 12:20:04 +02:00
< a routerLink = "./" [ fragment ] = " tab.id " queryParamsHandling = "merge" [ ngClass ] = " tab.customClass " >
< span * ngIf = "tab.title" > { { tab . title } } < / span >
< ng - container * ngTemplateOutlet = "tab.tabTemplate" > < / n g - c o n t a i n e r >
< / a >
2022-10-21 16:06:44 +02:00
< / li >
< / n g - c o n t a i n e r >
< / ul >
< / div >
2023-02-09 11:09:49 +01:00
< slider - arrow * ngIf = "position === 'horizontal' && arrows" type = "previous" > < / s l i d e r - a r r o w >
< slider - arrow * ngIf = "position === 'horizontal' && arrows" type = "next" > < / s l i d e r - a r r o w >
2022-10-21 16:06:44 +02:00
< / div >
` ,
} )
export class SliderTabsComponent implements AfterViewInit , OnDestroy {
/ * *
2023-02-09 11:09:49 +01:00
* Type of tabs :
* Static = Uikit tabs with @connect class or selector
* Dynamic = Active is defined by tabComponent ' s active Input
2022-10-21 16:06:44 +02:00
* Scrollable = Active is defined by the active fragment of URL and position of scroll
* * /
@Input ( )
public type : 'static' | 'dynamic' | 'scrollable' = 'static' ;
/ * *
2022-10-24 15:37:05 +02:00
* Connect selector in static type . Default : .uk - switcher
2022-10-21 16:06:44 +02:00
* * /
@Input ( )
2022-10-24 15:37:05 +02:00
public connect = '.uk-switcher' ;
2022-10-21 16:06:44 +02:00
/ * *
* Threshold between 0.0 to 1.0 for Intersection Observer
* * /
@Input ( )
public scrollThreshold = 0.1 ;
/ * *
* Tabs view : Horizontal is the default .
* * /
@Input ( )
public position : 'horizontal' | 'left' | 'right' = 'horizontal' ;
2022-10-24 15:37:05 +02:00
/ * *
* Tabs flex position : Left is the default .
* * /
@Input ( )
public flexPosition : 'center' | 'left' | 'right' = 'left' ;
2022-10-21 16:06:44 +02:00
/ * *
2023-02-09 11:09:49 +01:00
* Set a class above tabs
2022-10-21 16:06:44 +02:00
* * /
@Input ( )
2022-10-24 11:57:52 +02:00
public customClass : string ;
2023-02-09 11:09:49 +01:00
/ * *
* Tabs class
* * /
@Input ( )
public tabsClass : string = 'uk-tab' ;
@Input ( )
public border : boolean = true ;
@Input ( )
public arrows : boolean = true ;
2022-10-21 16:06:44 +02:00
@ContentChildren ( SliderTabComponent ) tabs : QueryList < SliderTabComponent > ;
2022-10-24 15:37:05 +02:00
@ViewChild ( 'sliderElement' ) sliderElement : ElementRef ;
2022-10-21 16:06:44 +02:00
@ViewChild ( 'tabsElement' ) tabsElement : ElementRef ;
2023-02-15 10:53:25 +01:00
private slider ;
2022-10-21 16:06:44 +02:00
/ * *
* Notify regarding new active element
* * /
@Output ( ) activeEmitter : EventEmitter < string > = new EventEmitter < string > ( ) ;
2022-10-24 15:37:05 +02:00
private activeIndex : number = 0 ;
2022-10-21 16:06:44 +02:00
private subscriptions : any [ ] = [ ] ;
private observer : IntersectionObserver ;
private timeout : Timeout ;
constructor ( private route : ActivatedRoute ,
private router : Router ,
private cdr : ChangeDetectorRef ) {
}
ngAfterViewInit() {
2023-07-19 13:09:10 +02:00
this . initTabs ( ) ;
this . tabs . changes . subscribe ( ( ) = > {
this . initTabs ( ) ;
} ) ;
}
public initTabs() {
if ( typeof document !== 'undefined' && this . tabs . length > 0 ) {
setTimeout ( ( ) = > {
if ( this . position === 'horizontal' ) {
this . slider = UIkit . slider ( this . sliderElement . nativeElement , { finite : true } ) ;
this . slider . clsActive = 'uk-slider-active' ;
this . slider . updateActiveClasses ( ) ;
this . slider . slides . forEach ( ( item , index ) = > {
if ( ! this . tabs . get ( index ) . active ) {
item . classList . remove ( 'uk-active' ) ;
2023-02-09 11:09:49 +01:00
}
2023-07-19 13:09:10 +02:00
} ) ;
if ( this . type === 'static' ) {
let tabs = UIkit . tab ( this . tabsElement . nativeElement , { connect : this.connect } ) ;
tabs . show ( this . activeIndex ) ;
if ( this . connect . includes ( '#' ) ) {
this . scrollToStart ( ) ;
}
} else if ( this . type == 'dynamic' ) {
this . activeIndex = this . tabs . toArray ( ) . findIndex ( tab = > tab . active ) ;
this . slider . show ( this . activeIndex ) ;
} else if ( this . type === 'scrollable' ) {
this . scrollable ( this . slider ) ;
2022-10-21 16:06:44 +02:00
}
2023-07-19 13:09:10 +02:00
} else {
this . scrollable ( ) ;
}
} ) ;
}
2022-10-21 16:06:44 +02:00
}
2022-10-24 15:37:05 +02:00
private scrollToStart() {
this . subscriptions . push ( UIkit . util . on ( this . connect , 'shown' , ( event ) : void = > {
let index = event . detail [ 0 ] . index ( ) ;
if ( index !== this . activeIndex ) {
this . activeIndex = index ;
2023-01-27 12:54:40 +01:00
this . router . navigate ( [ './' ] , { relativeTo : this.route , fragment : this.connect.replace ( '#' , '' ) , queryParamsHandling : "merge" } ) ;
2022-10-24 15:37:05 +02:00
}
} ) ) ;
}
2022-10-21 16:06:44 +02:00
private scrollable ( slider = null ) {
this . activeFragment ( this . route . snapshot . fragment , slider ) ;
this . subscriptions . push ( this . route . fragment . subscribe ( fragment = > {
2022-10-24 15:37:05 +02:00
this . activeFragment ( fragment , slider ) ;
2022-10-21 16:06:44 +02:00
} ) ) ;
this . setObserver ( ) ;
}
private setObserver() {
if ( this . observer ) {
this . observer . disconnect ( ) ;
}
this . observer = new IntersectionObserver ( ( entries ) = > {
entries . forEach ( entry = > {
if ( entry . isIntersecting ) {
if ( this . timeout ) {
clearTimeout ( this . timeout ) ;
}
this . timeout = setTimeout ( ( ) = > {
this . router . navigate ( [ './' ] , {
fragment : entry.target.id ,
relativeTo : this.route ,
2023-01-27 12:54:40 +01:00
state : { disableScroll : true } ,
queryParamsHandling : 'merge'
2022-10-21 16:06:44 +02:00
} ) ;
} , 200 ) ;
}
} ) ;
} , { threshold : 0.1 } ) ;
this . tabs . forEach ( tab = > {
let element = document . getElementById ( tab . id ) ;
if ( element ) {
this . observer . observe ( element ) ;
}
} ) ;
}
2023-02-15 10:53:25 +01:00
public showActive ( index ) {
this . activeIndex = index ;
2023-03-27 13:59:17 +02:00
this . activeEmitter . emit ( this . tabs . get ( this . activeIndex ) . id ) ;
2023-02-15 10:53:25 +01:00
if ( this . slider ) {
this . slider . show ( this . activeIndex ) ;
}
}
2022-10-21 16:06:44 +02:00
private activeFragment ( fragment , slider ) {
let index = 0 ;
if ( fragment ) {
index = this . tabs . toArray ( ) . findIndex ( item = > item . id == fragment ) ;
}
2022-10-24 15:37:05 +02:00
if ( slider ) {
2022-10-21 16:06:44 +02:00
slider . show ( index ) ;
}
this . tabs . forEach ( ( tab , i ) = > {
if ( index === i ) {
tab . active = true ;
this . activeEmitter . emit ( tab . id ) ;
} else {
tab . active = false ;
}
} ) ;
this . cdr . detectChanges ( ) ;
}
2023-01-27 12:54:40 +01:00
get leftTabs ( ) : SliderTabComponent [ ] {
return this . tabs . toArray ( ) . filter ( tab = > tab . align === 'left' ) ;
}
get rightTabs ( ) : SliderTabComponent [ ] {
return this . tabs . toArray ( ) . filter ( tab = > tab . align === 'right' ) ;
}
2022-10-21 16:06:44 +02:00
ngOnDestroy() {
this . subscriptions . forEach ( subscription = > {
if ( subscription instanceof Subscription ) {
subscription . unsubscribe ( ) ;
2022-10-24 15:37:05 +02:00
} else if ( subscription instanceof Function ) {
subscription ( ) ;
2022-10-21 16:06:44 +02:00
}
} ) ;
if ( this . observer ) {
this . observer . disconnect ( ) ;
}
}
}