feat(carousel): add circular scrolling support and improve button disable logic

This commit is contained in:
2026-07-23 11:53:56 -03:00
parent 326f2d9bcd
commit fb6ab2fca2
4 changed files with 78 additions and 2 deletions

View File

@@ -39,15 +39,23 @@ export class CarouselComponent<T> implements AfterViewInit {
readonly trackBy = input<CarouselTrackBy<T>>((index) => index);
readonly scrollStep = input<number | null>(null);
readonly gap = input(0);
readonly circular = input(false);
readonly ariaLabel = input('Carrusel');
readonly previousLabel = input('Mostrar elementos anteriores');
readonly nextLabel = input('Mostrar elementos siguientes');
protected readonly canScrollPrevious = signal(false);
protected readonly canScrollNext = signal(false);
protected readonly hasOverflow = signal(false);
protected readonly viewportWidth = signal<number | null>(null);
protected readonly hasMultipleItems = computed(() => this.items().length > 1);
protected readonly resolvedGap = computed(() => Math.max(this.gap(), 0));
protected readonly isPreviousDisabled = computed(
() => !this.hasOverflow() || (!this.circular() && !this.canScrollPrevious()),
);
protected readonly isNextDisabled = computed(
() => !this.hasOverflow() || (!this.circular() && !this.canScrollNext()),
);
ngAfterViewInit(): void {
if (!isPlatformBrowser(this.platformId)) {
@@ -74,10 +82,20 @@ export class CarouselComponent<T> implements AfterViewInit {
}
previous(): void {
if (this.circular() && this.hasOverflow() && !this.canScrollPrevious()) {
this.scrollToEdge('end');
return;
}
this.scroll(-1);
}
next(): void {
if (this.circular() && this.hasOverflow() && !this.canScrollNext()) {
this.scrollToEdge('start');
return;
}
this.scroll(1);
}
@@ -126,10 +144,20 @@ export class CarouselComponent<T> implements AfterViewInit {
const maxScrollLeft = Math.max(viewport.scrollWidth - viewport.clientWidth, 0);
const tolerance = 1;
this.hasOverflow.set(maxScrollLeft > tolerance);
this.canScrollPrevious.set(viewport.scrollLeft > tolerance);
this.canScrollNext.set(viewport.scrollLeft < maxScrollLeft - tolerance);
}
private scrollToEdge(edge: 'start' | 'end'): void {
const viewport = this.viewport().nativeElement;
viewport.scrollTo({
left: edge === 'start' ? 0 : viewport.scrollWidth - viewport.clientWidth,
behavior: 'smooth',
});
}
private refreshLayout(): void {
const viewport = this.viewport().nativeElement;
const firstItem = viewport.firstElementChild as HTMLElement | null;