openaire-library/sharedComponents/tabs/slider-tabs.component.ts

203 lines
6.4 KiB
TypeScript

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: `
<div #sliderElement class="uk-position-relative" [class.uk-slider]="position === 'horizontal'"
[ngClass]="customClass">
<div [class.uk-slider-container-tabs]="position === 'horizontal'">
<ul #tabsElement class="uk-tab" [class.uk-flex-nowrap]="position === 'horizontal'"
[class.uk-slider-items]="position === 'horizontal'"
[class.uk-tab-left]="position === 'left'" [class.uk-tab-right]="position === 'right'"
[attr.uk-tab]="type === 'static'?('connect:' + connect):null"
[ngClass]="'uk-flex-' + flexPosition">
<ng-container *ngIf="type === 'static'">
<li *ngFor="let tab of tabs.toArray()" class="uk-text-capitalize">
<a>{{tab.title}}</a>
</li>
</ng-container>
<ng-container *ngIf="type === 'scrollable'">
<li *ngFor="let tab of tabs.toArray()" class="uk-text-capitalize" [class.uk-active]="tab.active">
<a routerLink="./" [fragment]="tab.id">{{tab.title}}</a>
</li>
</ng-container>
</ul>
</div>
<a *ngIf="position === 'horizontal'" class="uk-position-center-left uk-blur-background" uk-slider-item="previous"><span
uk-icon="chevron-left"></span></a>
<a *ngIf="position === 'horizontal'" class="uk-position-center-right uk-blur-background"
uk-slider-item="next"><span
uk-icon="chevron-right"></span></a>
</div>
`,
})
export class SliderTabsComponent implements AfterViewInit, OnDestroy {
//TODO now it works only for scrollable and static, to be extended for dynamic
/**
* Type of tabs: Static = Uikit tabs with @connect class or selector, Dynamic = Active is defined by tabComponent.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';
/**
* Tabs custom class
* */
@Input()
public customClass: string;
@ContentChildren(SliderTabComponent) tabs: QueryList<SliderTabComponent>;
@ViewChild('sliderElement') sliderElement: ElementRef;
@ViewChild('tabsElement') tabsElement: ElementRef;
/**
* Notify regarding new active element
* */
@Output() activeEmitter: EventEmitter<string> = new EventEmitter<string>();
private activeIndex: number = 0;
private subscriptions: any[] = [];
private observer: IntersectionObserver;
private timeout: Timeout;
constructor(private route: ActivatedRoute,
private router: Router,
private cdr: ChangeDetectorRef) {
}
ngAfterViewInit() {
if (typeof document !== 'undefined' && this.tabs.length > 0) {
setTimeout(() => {
if (this.position === 'horizontal') {
let slider = UIkit.slider(this.sliderElement.nativeElement, {finite: true});
slider.clsActive = 'uk-slider-active';
slider.updateActiveClasses();
slider.slides.forEach(item => {
item.classList.remove('uk-active');
});
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 === 'scrollable') {
this.scrollable(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('#', '')});
}
}));
}
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}
});
}, 200);
}
});
}, {threshold: 0.1});
this.tabs.forEach(tab => {
let element = document.getElementById(tab.id);
if (element) {
this.observer.observe(element);
}
});
}
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();
}
ngOnDestroy() {
this.subscriptions.forEach(subscription => {
if (subscription instanceof Subscription) {
subscription.unsubscribe();
} else if (subscription instanceof Function) {
subscription();
}
});
if (this.observer) {
this.observer.disconnect();
}
}
}