mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
2.8 KiB
108 lines
2.8 KiB
import {
|
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
ChangeDetectionStrategy,
|
|
Component,
|
|
contentChildren,
|
|
ElementRef,
|
|
HostBinding,
|
|
Inject,
|
|
Input,
|
|
Optional,
|
|
ViewChild
|
|
} from '@angular/core';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
|
|
import { IonIcon } from '@ionic/angular/standalone';
|
|
import { addIcons } from 'ionicons';
|
|
import { chevronBackOutline, chevronForwardOutline } from 'ionicons/icons';
|
|
|
|
@Component({
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
imports: [IonIcon, MatButtonModule],
|
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
selector: 'gf-carousel',
|
|
styleUrls: ['./carousel.component.scss'],
|
|
templateUrl: './carousel.component.html'
|
|
})
|
|
export class GfCarouselComponent {
|
|
@HostBinding('class.animations-disabled')
|
|
public readonly animationsDisabled: boolean;
|
|
|
|
@Input('aria-label') public ariaLabel: string | undefined;
|
|
|
|
@ViewChild('list') public list!: ElementRef<HTMLElement>;
|
|
|
|
public items = contentChildren('carouselItem', { read: ElementRef });
|
|
public showPrevArrow = false;
|
|
public showNextArrow = true;
|
|
|
|
private index = 0;
|
|
private position = 0;
|
|
|
|
public constructor(
|
|
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationsModule?: string
|
|
) {
|
|
this.animationsDisabled = animationsModule === 'NoopAnimations';
|
|
|
|
addIcons({ chevronBackOutline, chevronForwardOutline });
|
|
}
|
|
|
|
public next() {
|
|
for (let i = this.index; i < this.items().length; i++) {
|
|
if (this.isOutOfView(i)) {
|
|
this.index = i;
|
|
this.scrollToActiveItem();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public previous() {
|
|
for (let i = this.index; i > -1; i--) {
|
|
if (this.isOutOfView(i)) {
|
|
this.index = i;
|
|
this.scrollToActiveItem();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private isOutOfView(index: number, side?: 'start' | 'end') {
|
|
const { offsetWidth, offsetLeft } = this.items()[index].nativeElement;
|
|
|
|
if ((!side || side === 'start') && offsetLeft - this.position < 0) {
|
|
return true;
|
|
}
|
|
|
|
return (
|
|
(!side || side === 'end') &&
|
|
offsetWidth + offsetLeft - this.position >
|
|
this.list.nativeElement.clientWidth
|
|
);
|
|
}
|
|
|
|
private scrollToActiveItem() {
|
|
if (!this.isOutOfView(this.index)) {
|
|
return;
|
|
}
|
|
|
|
let targetItemIndex = this.index;
|
|
|
|
if (this.index > 0 && !this.isOutOfView(this.index - 1)) {
|
|
targetItemIndex =
|
|
this.items().findIndex((_, i) => !this.isOutOfView(i)) + 1;
|
|
}
|
|
|
|
this.position = this.items()[targetItemIndex].nativeElement.offsetLeft;
|
|
this.list.nativeElement.style.transform = `translateX(-${this.position}px)`;
|
|
this.showPrevArrow = this.index > 0;
|
|
this.showNextArrow = false;
|
|
|
|
for (let i = this.items().length - 1; i > -1; i--) {
|
|
if (this.isOutOfView(i, 'end')) {
|
|
this.showNextArrow = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|