poc: more responsive carousel pagination dots (#2220)

* chore(carousel): more responsive dots

* chore: minor change

* chore: update

* chore: update

* chore: minor change
This commit is contained in:
Alessandro
2024-10-24 22:25:14 +02:00
committed by GitHub
parent ee79a46751
commit ae30d40e62

View File

@@ -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();
@@ -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. */
@@ -277,20 +281,28 @@ export default class SlCarousel extends ShoelaceElement {
}
const firstIntersecting = entries.find(entry => entry.isIntersecting);
if (!firstIntersecting) {
return;
}
if (firstIntersecting) {
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;
// 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;
}
}
},
@@ -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 {
@@ -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);
@@ -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() {