From 42daf66b69c14e54774c60abef05e64f90c525a3 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Tue, 24 Jun 2025 12:05:32 +0530 Subject: [PATCH 1/4] WP-144 add logic for scroll snap and remove swipe support --- src/slider/index.html | 10 ++-- src/slider/style.scss | 4 ++ src/slider/tp-slider.ts | 127 ++++++++++++++++++---------------------- 3 files changed, 65 insertions(+), 76 deletions(-) diff --git a/src/slider/index.html b/src/slider/index.html index c53dc8a..421a93e 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -33,7 +33,7 @@
- + @@ -63,7 +63,7 @@
- + @@ -92,7 +92,7 @@
- + @@ -162,14 +162,14 @@ - + - + diff --git a/src/slider/style.scss b/src/slider/style.scss index f78544f..e27b1ec 100644 --- a/src/slider/style.scss +++ b/src/slider/style.scss @@ -13,6 +13,10 @@ tp-slider-slides { position: relative; display: flex; align-items: flex-start; + overflow-y: visible; + overflow-x: auto; + scroll-snap-type: x mandatory; + scrollbar-width: none; tp-slider:not([resizing="yes"]) & { transition-duration: 0.6s; diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index a5fa1ac..0055c1f 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -15,9 +15,8 @@ export class TPSliderElement extends HTMLElement { /** * Properties. */ - protected touchStartX: number = 0; - protected touchStartY: number = 0; - protected swipeThreshold: number = 200; + protected slidesScrollContainer: TPSliderSlidesElement | null; + protected slidesScrollContainerRect: DOMRect | undefined; protected responsiveSettings: { [ key: string ]: any }; protected allowedResponsiveKeys: string[] = [ 'flexible-height', @@ -36,15 +35,17 @@ export class TPSliderElement extends HTMLElement { constructor() { // Initialize parent. super(); + this.slidesScrollContainer = this.querySelector( 'tp-slider-slides' ); + this.slidesScrollContainerRect = this.slidesScrollContainer?.getBoundingClientRect(); + + // Add event listener to handle current slide attribute on scroll. + this.slidesScrollContainer?.addEventListener( 'scroll', this.handleCurrentSlideOnScroll.bind( this ) ); // Set current slide. if ( ! this.getAttribute( 'current-slide' ) ) { this.setAttribute( 'current-slide', '1' ); } - // Threshold Setting. - this.swipeThreshold = Number( this?.getAttribute( 'swipe-threshold' ) ?? '200' ); - // Initialize slider. this.slide(); this.autoSlide(); @@ -65,10 +66,6 @@ export class TPSliderElement extends HTMLElement { window.addEventListener( 'resize', this.handleResize.bind( this ) ); document.fonts.ready.then( () => this.handleResize() ); } - - // Touch listeners. - this.addEventListener( 'touchstart', this.handleTouchStart.bind( this ), { passive: true } ); - this.addEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); } /** @@ -92,7 +89,7 @@ export class TPSliderElement extends HTMLElement { */ static get observedAttributes(): string[] { // Observed attributes. - return [ 'current-slide', 'flexible-height', 'infinite', 'swipe', 'per-view', 'step' ]; + return [ 'current-slide', 'flexible-height', 'infinite', 'per-view', 'step' ]; } /** @@ -348,7 +345,6 @@ export class TPSliderElement extends HTMLElement { } // First, update the height. - // Yield to main thread to fix a bug in Safari 16. setTimeout( () => this.updateHeight(), 0 ); @@ -358,7 +354,10 @@ export class TPSliderElement extends HTMLElement { // Check if behaviour is set to fade and slide on the current slide index is present in the slides array. if ( 'fade' !== behaviour && slides[ this.currentSlideIndex - 1 ] ) { // Yes, it is. So slide to the current slide. - slidesContainer.style.left = `-${ slides[ this.currentSlideIndex - 1 ].offsetLeft }px`; + slidesContainer.scroll( { + left: slides[ this.currentSlideIndex - 1 ].offsetLeft - slides[ 0 ].offsetLeft, + behavior: 'smooth', + } ); } } @@ -487,6 +486,50 @@ export class TPSliderElement extends HTMLElement { } } + /** + * Handle current slide attribute on scroll. + * + * This is to handle the case when the user scrolls the slides track + * and we need to update the current slide index based on the scroll position. + */ + handleCurrentSlideOnScroll() { + // Check if we have scroll container. + if ( ! this.slidesScrollContainer ) { + // No scroll container, early return. + return; + } + + // Get sll the slides. + const slides : NodeListOf | null = this.querySelectorAll( 'tp-slider-slide' ); + + // If no slides. + if ( ! slides ) { + // Early return. + return; + } + + // Check if scroll position is at right end. + const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 1; + + // Conditionally set the current slide index based on the scroll position. + if ( isAtRightEnd ) { + // If the current slide index is equal to the total number of slides, set it to the last slide. + this.setCurrentSlide( slides.length ); + } else { + // Loop through all slides and check which slide is intersecting with the left edge of the scroll container. + slides.forEach( ( slide: TPSliderSlideElement, index: number ) => { + // Get the bounding rectangle of the slide. + const slideRect = slide.getBoundingClientRect(); + + // Check if the slide is intersecting with the left edge of the scroll container. + if ( this.slidesScrollContainerRect && slideRect?.left - this.slidesScrollContainerRect.left === 0 ) { + // Yes, it is. So set the current slide index. + this.setCurrentSlide( index + 1 ); + } + } ); + } + } + /** * Update the height of the slider based on current slide. */ @@ -624,64 +667,6 @@ export class TPSliderElement extends HTMLElement { } ); } - /** - * Detect touch start event, and store the starting location. - * - * @param {Event} e Touch event. - * - * @protected - */ - protected handleTouchStart( e: TouchEvent ): void { - // initialize touch start coordinates - if ( 'yes' === this.getAttribute( 'swipe' ) ) { - this.touchStartX = e.touches[ 0 ].clientX; - this.touchStartY = e.touches[ 0 ].clientY; - } - } - - /** - * Detect touch end event, and check if it was a left or right swipe. - * - * @param {Event} e Touch event. - * - * @protected - */ - protected handleTouchEnd( e: TouchEvent ): void { - // Early return if swipe is not enabled. - if ( 'yes' !== this.getAttribute( 'swipe' ) ) { - // Early return. - return; - } - - // Calculate the horizontal and vertical distance moved. - const touchEndX: number = e.changedTouches[ 0 ].clientX; - const touchEndY: number = e.changedTouches[ 0 ].clientY; - const swipeDistanceX: number = touchEndX - this.touchStartX; - const swipeDistanceY: number = touchEndY - this.touchStartY; - - // Determine if the swipe is predominantly horizontal or vertical. - const isHorizontalSwipe: boolean = Math.abs( swipeDistanceX ) > Math.abs( swipeDistanceY ); - - // If it's not horizontal swipe, return - if ( ! isHorizontalSwipe ) { - // Early return. - return; - } - - // Check if it's a right or left swipe. - if ( swipeDistanceX > 0 ) { - // Right-Swipe: Check if horizontal swipe distance is less than the threshold. - if ( swipeDistanceX < this.swipeThreshold ) { - this.previous(); - } - } else if ( swipeDistanceX < 0 ) { - // Left-Swipe: Check if horizontal swipe distance is less than the threshold. - if ( swipeDistanceX > -this.swipeThreshold ) { - this.next(); - } - } - } - /** * Auto slide. */ From 7215f97520ff9faaf7c4175464e841701af83472 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Tue, 24 Jun 2025 12:10:32 +0530 Subject: [PATCH 2/4] WP-144 Remove lint errors --- src/slider/tp-slider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 0055c1f..0f96e81 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -345,6 +345,7 @@ export class TPSliderElement extends HTMLElement { } // First, update the height. + // Yield to main thread to fix a bug in Safari 16. setTimeout( () => this.updateHeight(), 0 ); @@ -512,7 +513,7 @@ export class TPSliderElement extends HTMLElement { const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 1; // Conditionally set the current slide index based on the scroll position. - if ( isAtRightEnd ) { + if ( isAtRightEnd ) { // If the current slide index is equal to the total number of slides, set it to the last slide. this.setCurrentSlide( slides.length ); } else { @@ -522,7 +523,7 @@ export class TPSliderElement extends HTMLElement { const slideRect = slide.getBoundingClientRect(); // Check if the slide is intersecting with the left edge of the scroll container. - if ( this.slidesScrollContainerRect && slideRect?.left - this.slidesScrollContainerRect.left === 0 ) { + if ( this.slidesScrollContainerRect && slideRect?.left - this.slidesScrollContainerRect.left === 0 ) { // Yes, it is. So set the current slide index. this.setCurrentSlide( index + 1 ); } From 974468a47dbff574d6fe78d7e5eb81df75105449 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Fri, 4 Jul 2025 14:59:18 +0530 Subject: [PATCH 3/4] WP-144 Add improvements to code --- src/slider/tp-slider.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 0f96e81..04ed663 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -16,7 +16,7 @@ export class TPSliderElement extends HTMLElement { * Properties. */ protected slidesScrollContainer: TPSliderSlidesElement | null; - protected slidesScrollContainerRect: DOMRect | undefined; + protected slidesScrollContainerRect: DOMRect | {}; protected responsiveSettings: { [ key: string ]: any }; protected allowedResponsiveKeys: string[] = [ 'flexible-height', @@ -36,7 +36,7 @@ export class TPSliderElement extends HTMLElement { // Initialize parent. super(); this.slidesScrollContainer = this.querySelector( 'tp-slider-slides' ); - this.slidesScrollContainerRect = this.slidesScrollContainer?.getBoundingClientRect(); + this.slidesScrollContainerRect = this.slidesScrollContainer?.getBoundingClientRect() ?? {}; // Add event listener to handle current slide attribute on scroll. this.slidesScrollContainer?.addEventListener( 'scroll', this.handleCurrentSlideOnScroll.bind( this ) ); @@ -500,17 +500,17 @@ export class TPSliderElement extends HTMLElement { return; } - // Get sll the slides. + // Get all the slides. const slides : NodeListOf | null = this.querySelectorAll( 'tp-slider-slide' ); // If no slides. - if ( ! slides ) { + if ( 0 === slides.length ) { // Early return. return; } // Check if scroll position is at right end. - const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 1; + const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 2; // Conditionally set the current slide index based on the scroll position. if ( isAtRightEnd ) { @@ -522,8 +522,11 @@ export class TPSliderElement extends HTMLElement { // Get the bounding rectangle of the slide. const slideRect = slide.getBoundingClientRect(); + // Check if the slide is aligned with the left edge of the scroll container. + const isAlignedWithLeftEdge = Math.abs( slideRect?.left - ( this.slidesScrollContainerRect as DOMRect ).left ) < 1; + // Check if the slide is intersecting with the left edge of the scroll container. - if ( this.slidesScrollContainerRect && slideRect?.left - this.slidesScrollContainerRect.left === 0 ) { + if ( isAlignedWithLeftEdge ) { // Yes, it is. So set the current slide index. this.setCurrentSlide( index + 1 ); } From f03d79eb5a23d5232e5ad444d5190386456241f9 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Tue, 22 Jul 2025 12:47:57 +0530 Subject: [PATCH 4/4] WP-144 Optimise code --- src/slider/tp-slider.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 04ed663..d7d6825 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -28,6 +28,7 @@ export class TPSliderElement extends HTMLElement { 'step', 'responsive', ]; + protected isProgramaticScroll: boolean = false; /** * Constructor. @@ -68,6 +69,18 @@ export class TPSliderElement extends HTMLElement { } } + /** + * Flag programmatic scroll to prevent scroll handling. + */ + protected flagProgramaticScroll(): void { + // Flag programmatic scroll. + this.isProgramaticScroll = true; + setTimeout( () => { + // Unflag programmatic scroll. + this.isProgramaticScroll = false; + }, 500 ); + } + /** * Connected callback. */ @@ -328,6 +341,9 @@ export class TPSliderElement extends HTMLElement { * @protected */ protected slide(): void { + // Flag programmatic scroll. + this.flagProgramaticScroll(); + // Check if slider is disabled. if ( 'yes' === this.getAttribute( 'disabled' ) ) { // Yes, it is. So stop. @@ -494,6 +510,12 @@ export class TPSliderElement extends HTMLElement { * and we need to update the current slide index based on the scroll position. */ handleCurrentSlideOnScroll() { + // Check if this is a programmatic scroll. + if ( this.isProgramaticScroll ) { + // Early return to prevent handling programmatic scroll. + return; + } + // Check if we have scroll container. if ( ! this.slidesScrollContainer ) { // No scroll container, early return. @@ -510,7 +532,7 @@ export class TPSliderElement extends HTMLElement { } // Check if scroll position is at right end. - const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 2; + const isAtRightEnd = Math.abs( this.slidesScrollContainer.scrollLeft + this.slidesScrollContainer.clientWidth - this.slidesScrollContainer.scrollWidth ) < 1; // Conditionally set the current slide index based on the scroll position. if ( isAtRightEnd ) {