Skip to content

Commit

Permalink
poc: more responsive carousel pagination dots (#2220)
Browse files Browse the repository at this point in the history
* chore(carousel): more responsive dots

* chore: minor change

* chore: update

* chore: update

* chore: minor change
  • Loading branch information
alenaksu authored Oct 24, 2024
1 parent ee79a46 commit ae30d40
Showing 1 changed file with 31 additions and 17 deletions.
48 changes: 31 additions & 17 deletions src/components/carousel/carousel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default class SlCarousel extends ShoelaceElement {
private autoplayController = new AutoplayController(this, () => this.next());
private readonly localize = new LocalizeController(this);
private mutationObserver: MutationObserver;
private pendingSlideChange = false;

connectedCallback(): void {
super.connectedCallback();
Expand Down Expand Up @@ -261,6 +262,9 @@ export default class SlCarousel extends ShoelaceElement {
@eventOptions({ passive: true })
private handleScroll() {
this.scrolling = true;
if (!this.pendingSlideChange) {
this.synchronizeSlides();
}
}

/** @internal Synchronizes the slides with the IntersectionObserver API. */
Expand All @@ -277,20 +281,28 @@ export default class SlCarousel extends ShoelaceElement {
}

const firstIntersecting = entries.find(entry => entry.isIntersecting);
if (!firstIntersecting) {
return;
}

const slidesWithClones = this.getSlides({ excludeClones: false });
const slidesCount = this.getSlides().length;

// Update the current index based on the first visible slide
const slideIndex = slidesWithClones.indexOf(firstIntersecting.target as SlCarouselItem);
// Normalize the index to ignore clones
const normalizedIndex = this.loop ? slideIndex - this.slidesPerPage : slideIndex;

if (firstIntersecting) {
// Set the index to the closest "snappable" slide
this.activeSlide =
(Math.ceil(normalizedIndex / this.slidesPerMove) * this.slidesPerMove + slidesCount) % slidesCount;

if (!this.scrolling) {
if (this.loop && firstIntersecting.target.hasAttribute('data-clone')) {
const clonePosition = Number(firstIntersecting.target.getAttribute('data-clone'));

// Scrolls to the original slide without animating, so the user won't notice that the position has changed
this.goToSlide(clonePosition, 'instant');
} else {
const slides = this.getSlides();

// Update the current index based on the first visible slide
const slideIndex = slides.indexOf(firstIntersecting.target as SlCarouselItem);
// Set the index to the first "snappable" slide
this.activeSlide = Math.ceil(slideIndex / this.slidesPerMove) * this.slidesPerMove;
}
}
},
Expand All @@ -307,10 +319,9 @@ export default class SlCarousel extends ShoelaceElement {

private handleScrollEnd() {
if (!this.scrolling || this.dragging) return;

this.synchronizeSlides();

this.scrolling = false;
this.pendingSlideChange = false;
this.synchronizeSlides();
}

private isCarouselItem(node: Node): node is SlCarouselItem {
Expand Down Expand Up @@ -380,7 +391,7 @@ export default class SlCarousel extends ShoelaceElement {
}

@watch('activeSlide')
handelSlideChange() {
handleSlideChange() {
const slides = this.getSlides();
slides.forEach((slide, i) => {
slide.classList.toggle('--is-active', i === this.activeSlide);
Expand Down Expand Up @@ -485,11 +496,14 @@ export default class SlCarousel extends ShoelaceElement {
const nextLeft = nextSlideRect.left - scrollContainerRect.left;
const nextTop = nextSlideRect.top - scrollContainerRect.top;

scrollContainer.scrollTo({
left: nextLeft + scrollContainer.scrollLeft,
top: nextTop + scrollContainer.scrollTop,
behavior
});
if (nextLeft || nextTop) {
this.pendingSlideChange = true;
scrollContainer.scrollTo({
left: nextLeft + scrollContainer.scrollLeft,
top: nextTop + scrollContainer.scrollTop,
behavior
});
}
}

render() {
Expand Down

0 comments on commit ae30d40

Please sign in to comment.