openaire-library/sharedComponents/slider-utils/slider-container.component.ts

131 lines
3.9 KiB
TypeScript

import {
AfterContentInit,
ChangeDetectorRef,
Component,
ContentChildren,
ElementRef,
Input, OnDestroy,
OnInit,
QueryList
} from "@angular/core";
import {SliderColumnComponent} from "./slider-column.component";
export class Stage {
value: number;
max: number;
}
@Component({
selector: 'slider-container',
template: `
<div class="uk-grid uk-grid-large uk-flex uk-flex-middle uk-flex-between uk-child-width-1-1" [ngClass]="'uk-child-width-1-' + sliders?.length + '@m'" uk-grid>
<ng-content></ng-content>
</div>
<div *ngIf="navigation === 'progress' && stages.length > 0" class="uk-flex uk-flex-center uk-margin-large-top">
<div class="uk-width-1-3@m uk-width-1-2@s uk-child-width-1-1 uk-grid uk-grid-small uk-flex-middle" [ngClass]="'uk-child-width-1-' + stages.length" uk-grid>
<div *ngFor="let stage of stages; let i=index">
<progress (click)="start(i)" class="uk-progress" [value]="stage.value" [max]="stage.max"></progress>
</div>
</div>
</div>
`
})
export class SliderContainerComponent implements OnInit, OnDestroy, AfterContentInit {
private static INTERVAL = 10;
private static ANIMATION_DURATION = 600; // UIKit progress animation duration
@ContentChildren(SliderColumnComponent) sliders: QueryList<SliderColumnComponent>;
@Input()
navigation: 'progress' | null = null;
@Input()
total: number = 0;
@Input()
period: number = 3000; // in ms (>= 1000ms)
@Input()
infinite: boolean = false;
@Input()
parent: HTMLDivElement;
stages: Stage[] = [];
time: number = 0;
interval: any;
observer: IntersectionObserver;
initialized: boolean = false;
stopped: boolean = true;
constructor(private cdr: ChangeDetectorRef, private element: ElementRef) {
}
ngOnInit() {
this.period = this.period - SliderContainerComponent.ANIMATION_DURATION;
}
ngOnDestroy() {
if(this.observer && typeof IntersectionObserver !== 'undefined') {
this.observer.disconnect();
}
}
ngAfterContentInit() {
this.setObserver();
this.sliders.forEach(slider => {
slider.setContainer(this);
slider.navItems.forEach(item => {
if(this.parent) {
item.background = getComputedStyle(this.parent).backgroundColor;
}
});
});
}
setObserver() {
if(typeof IntersectionObserver !== 'undefined') {
let options = {
root: null,
rootMargin: '0px',
threshold: 0.1
};
this.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.initialized) {
this.stopped = false;
this.start(0);
this.initialized = true;
} else {
this.initialized = false;
this.stopped = true;
}
});
}, options);
this.observer.observe(this.element.nativeElement);
}
}
start(time: number) {
this.stages = [];
for(let i = 0; i < this.total; i++) {
this.stages.push({value: (time > i?this.period:(time - i)), max: this.period});
}
if(this.interval) {
clearInterval(this.interval);
}
this.cdr.detectChanges();
this.interval = setInterval(() => {
let current = Math.floor(time);
this.stages[current].value += SliderContainerComponent.INTERVAL;
this.time = current + this.stages[current].value/this.stages[current].max;
this.sliders.forEach(slider => {
slider.change(this.time);
});
if(this.stages[current].value >= this.stages[current].max) {
clearInterval(this.interval);
let next = (current + 1 > this.total - 1)?0:current + 1;
if(!this.stopped && (this.infinite || next > current)) {
setTimeout(() => {
this.start(next);
}, SliderContainerComponent.ANIMATION_DURATION);
}
}
}, SliderContainerComponent.INTERVAL);
}
}