import { AfterViewInit, ChangeDetectorRef, Component, ContentChildren, ElementRef, EventEmitter, Input, OnDestroy, Output, QueryList, ViewChild, Inject, PLATFORM_ID } from "@angular/core"; import {SliderTabComponent} from "./slider-tab.component"; import {ActivatedRoute, Router} from "@angular/router"; import {Subscription} from "rxjs"; import {isPlatformServer} from "@angular/common"; import Timeout = NodeJS.Timeout; declare var UIkit; @Component({ selector: 'slider-tabs', template: `
`, }) export class SliderTabsComponent implements AfterViewInit, OnDestroy { /** * Type of tabs: * Static = Uikit tabs with @connect class or selector * Dynamic = Active is defined by tabComponent's active Input * Scrollable = Active is defined by the active fragment of URL and position of scroll * */ @Input() public type: 'static' | 'dynamic' | 'scrollable' = 'static'; /** * Connect selector in static type. Default: .uk-switcher * */ @Input() public connect = '.uk-switcher'; /** * 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'; /** * Tabs flex position: Left is the default. * */ @Input() public flexPosition: 'center' | 'left' | 'right' = 'left'; /** * Set a class for the container * */ @Input() public containerClass: string; /** * Set a class above tabs * */ @Input() public customClass: string; /** * Tabs class * */ @Input() public tabsClass: string = 'uk-tab'; @Input() public border: boolean = true; @Input() public arrows: boolean = true; @ContentChildren(SliderTabComponent) tabs: QueryList; @ViewChild('sliderElement') sliderElement: ElementRef; @ViewChild('tabsElement') tabsElement: ElementRef; private slider; /** * Notify regarding new active element * */ @Output() activeEmitter: EventEmitter = new EventEmitter(); private activeIndex: number = 0; private subscriptions: any[] = []; private observer: IntersectionObserver; private timeout: Timeout; isServer: boolean; constructor(private route: ActivatedRoute, private router: Router, private cdr: ChangeDetectorRef, @Inject(PLATFORM_ID) private platform: any) { this.isServer = isPlatformServer(this.platform); } ngAfterViewInit() { 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'); } }); if (this.type === 'static') { let tabs = UIkit.switcher(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); } } else { this.scrollable(); } }); } } 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; this.router.navigate(['./'], {relativeTo: this.route, fragment: this.connect.replace('#', ''), queryParamsHandling: "merge"}); } })); } private scrollable(slider = null) { this.activeFragment(this.route.snapshot.fragment, slider); this.subscriptions.push(this.route.fragment.subscribe(fragment => { this.activeFragment(fragment, slider); })); 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, state: {disableScroll: true}, queryParamsHandling: 'merge' }); }, 200); } }); }, {threshold: 0.1}); this.tabs.forEach(tab => { let element = document.getElementById(tab.id); if (element) { this.observer.observe(element); } }); } public showActive(index) { this.activeIndex = index; this.activeEmitter.emit(this.tabs.get(this.activeIndex).id); if(this.slider) { this.slider.show(this.activeIndex); } } private activeFragment(fragment, slider) { let index = 0; if (fragment) { index = this.tabs.toArray().findIndex(item => item.id == fragment); } if (slider) { 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(); } get leftTabs(): SliderTabComponent[] { return this.tabs.toArray().filter(tab => tab.align === 'left'); } get rightTabs(): SliderTabComponent[] { return this.tabs.toArray().filter(tab => tab.align === 'right'); } ngOnDestroy() { this.subscriptions.forEach(subscription => { if (subscription instanceof Subscription) { subscription.unsubscribe(); } else if (subscription instanceof Function) { subscription(); } }); if (this.observer) { this.observer.disconnect(); } } }