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">
|
2022-10-21 16:06:44 +02:00
|
|
|
<div [class.uk-slider-container-tabs]="position === 'horizontal'">
|
2022-10-24 15:37:05 +02:00
|
|
|
<ul #tabsElement class="uk-tab" [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'"
|
2022-10-24 15:37:05 +02:00
|
|
|
[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>
|
2022-10-21 16:06:44 +02:00
|
|
|
<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>
|
2022-11-30 16:49:38 +01:00
|
|
|
<a *ngIf="position === 'horizontal'" class="uk-position-center-right uk-blur-background" uk-slider-item="next"><span
|
2022-10-21 16:06:44 +02:00
|
|
|
uk-icon="chevron-right"></span></a>
|
|
|
|
</div>
|
|
|
|
`,
|
|
|
|
})
|
|
|
|
export class SliderTabsComponent implements AfterViewInit, OnDestroy {
|
2022-10-24 15:37:05 +02:00
|
|
|
//TODO now it works only for scrollable and static, to be extended for dynamic
|
2022-10-21 16:06:44 +02:00
|
|
|
/**
|
2022-10-24 15:37:05 +02:00
|
|
|
* Type of tabs: Static = Uikit tabs with @connect class or selector, Dynamic = Active is defined by tabComponent.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
|
|
|
/**
|
2022-10-24 11:57:52 +02:00
|
|
|
* Tabs custom class
|
2022-10-21 16:06:44 +02:00
|
|
|
* */
|
|
|
|
@Input()
|
2022-10-24 11:57:52 +02:00
|
|
|
public customClass: string;
|
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;
|
|
|
|
/**
|
|
|
|
* 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() {
|
|
|
|
if (typeof document !== 'undefined' && this.tabs.length > 0) {
|
|
|
|
setTimeout(() => {
|
2022-10-24 15:37:05 +02:00
|
|
|
if (this.position === 'horizontal') {
|
|
|
|
let slider = UIkit.slider(this.sliderElement.nativeElement, {finite: true});
|
2022-10-21 16:06:44 +02:00
|
|
|
slider.clsActive = 'uk-slider-active';
|
|
|
|
slider.updateActiveClasses();
|
|
|
|
slider.slides.forEach(item => {
|
|
|
|
item.classList.remove('uk-active');
|
|
|
|
});
|
2022-10-24 15:37:05 +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 === 'scrollable') {
|
2022-10-21 16:06:44 +02:00
|
|
|
this.scrollable(slider);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.scrollable();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
this.router.navigate(['./'], {relativeTo: this.route, fragment: this.connect.replace('#', '')});
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
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);
|
|
|
|
}
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|