From a64fa71925b4002659aea1caaf2f22518a72ff0e Mon Sep 17 00:00:00 2001 From: Ashish Rawat Date: Sun, 16 Mar 2025 22:42:36 +0530 Subject: [PATCH 01/31] WP-144: TP Slider Component --- src/new-slider/index.html | 23 +++++++ src/new-slider/index.ts | 14 ++++ src/new-slider/style.scss | 50 ++++++++++++++ src/new-slider/tp-slider.ts | 127 ++++++++++++++++++++++++++++++++++++ webpack.config.js | 1 + 5 files changed, 215 insertions(+) create mode 100644 src/new-slider/index.html create mode 100644 src/new-slider/index.ts create mode 100644 src/new-slider/style.scss create mode 100644 src/new-slider/tp-slider.ts diff --git a/src/new-slider/index.html b/src/new-slider/index.html new file mode 100644 index 0000000..113157e --- /dev/null +++ b/src/new-slider/index.html @@ -0,0 +1,23 @@ + + + + + + + + + Slide 1 + Slide 2 + Slide 3 + Slide 1 + Slide 2 + Slide 3 + + + diff --git a/src/new-slider/index.ts b/src/new-slider/index.ts new file mode 100644 index 0000000..6b43961 --- /dev/null +++ b/src/new-slider/index.ts @@ -0,0 +1,14 @@ +/** + * Styles. + */ +import './style.scss'; + +/** + * Components. + */ +import { TPSlider } from './tp-slider'; + +/** + * Register Components. + */ +customElements.define( 'tp-slider', TPSlider ); diff --git a/src/new-slider/style.scss b/src/new-slider/style.scss new file mode 100644 index 0000000..57c983c --- /dev/null +++ b/src/new-slider/style.scss @@ -0,0 +1,50 @@ +tp-slider { + display: flex; + overflow-x: hidden; + scroll-snap-type: x mandatory; + position: relative; + gap: 15px; +} + +/* Arrows */ +.tp-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + cursor: pointer; + z-index: 10; + background: rgba(0, 0, 0, 0.5); + padding: 10px; + border-radius: 50%; +} +.tp-arrow.prev { + left: 10px; +} +.tp-arrow.next { + right: 10px; +} + +/* Dots/Pagination */ +.tp-dots { + position: absolute; + bottom: 10px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 8px; +} +.tp-dot { + width: 12px; + height: 12px; + border-radius: 50%; + background: #ccc; + cursor: pointer; +} +.tp-dot.active { + background: #007bff; +} + +/* Infinite Scroll Clones */ +.tp-slide.clone { + opacity: 0.5; +} diff --git a/src/new-slider/tp-slider.ts b/src/new-slider/tp-slider.ts new file mode 100644 index 0000000..fdaa3e6 --- /dev/null +++ b/src/new-slider/tp-slider.ts @@ -0,0 +1,127 @@ +export class TPSlider extends HTMLElement { + private slides: HTMLElement[]; + private currentIndex: number; + private autoPlayInterval: number | null; + private slidesPerView: number; + private dotsContainer!: HTMLDivElement; + + constructor() { + super(); + this.slides = []; + this.currentIndex = 0; + this.autoPlayInterval = null; + this.slidesPerView = 1; + this.init(); + } + + // Initialize Component + private init(): void { + this.setupSlides(); + if (this.hasAttribute("arrows")) this.addArrows(); + if (this.hasAttribute("dots")) this.addDots(); + if (this.hasAttribute("infinite")) this.addClones(); + if (this.hasAttribute("auto-play")) this.startAutoPlay(); + } + + // Setup Slides & Scroll + private setupSlides(): void { + this.slides = [...this.querySelectorAll("tp-slide")] as HTMLElement[]; + this.slidesPerView = parseInt(this.getAttribute("slides-per-view") || "1"); + this.style.overflowX = "auto"; + this.slides.forEach((slide) => { + slide.style.flex = `0 0 calc(100% / ${this.slidesPerView} - 10px)`; + slide.style.scrollSnapAlign = "start"; + }); + } + + // Next/Previous Methods + public next(): void { + this.currentIndex = (this.currentIndex + 1) % this.slides.length; + this.scrollToSlide(this.currentIndex); + } + + public prev(): void { + this.currentIndex = + (this.currentIndex - 1 + this.slides.length) % this.slides.length; + this.scrollToSlide(this.currentIndex); + } + + // Scroll to Specific Slide + public scrollToSlide(index: number): void { + const slideWidth = this.slides[0].offsetWidth + 10; // Include gap + this.scrollTo({ + left: slideWidth * index, + behavior: "smooth", + }); + this.updateDots(index); + } + + // Pagination Dots + private addDots(): void { + this.dotsContainer = document.createElement("div"); + this.dotsContainer.className = "tp-dots"; + this.slides.forEach((_, i) => { + const dot = document.createElement("div"); + dot.className = `tp-dot ${i === 0 ? "active" : ""}`; + dot.addEventListener("click", () => this.scrollToSlide(i)); + this.dotsContainer.appendChild(dot); + }); + this.appendChild(this.dotsContainer); + } + + // Update Active Dot + private updateDots(activeIndex: number): void { + if (!this.dotsContainer) return; + + const dots = this.dotsContainer.querySelectorAll(".tp-dot"); + dots.forEach((dot, i) => { + dot.classList.toggle("active", i === activeIndex); + }); + } + + // Infinite Scroll (Clone First/Last Slides) + private addClones(): void { + const firstClone = this.slides[0].cloneNode(true) as HTMLElement; + const lastClone = this.slides[this.slides.length - 1].cloneNode(true) as HTMLElement; + firstClone.classList.add("clone"); + lastClone.classList.add("clone"); + this.append(firstClone); + this.prepend(lastClone); + } + + // Auto-Play + private startAutoPlay(): void { + const delay = parseInt(this.getAttribute("auto-play") || "3000"); + this.autoPlayInterval = window.setInterval(() => this.next(), delay); + + this.addEventListener("mouseenter", () => { + if (this.autoPlayInterval) { + clearInterval(this.autoPlayInterval); + this.autoPlayInterval = null; + } + }); + + this.addEventListener("mouseleave", () => { + if (!this.autoPlayInterval) { + this.startAutoPlay(); + } + }); + } + + // Arrows + private addArrows(): void { + const prevArrow = document.createElement("div"); + prevArrow.className = "tp-arrow prev"; + prevArrow.innerHTML = "❮"; + prevArrow.addEventListener("click", () => this.prev()); + + const nextArrow = document.createElement("div"); + nextArrow.className = "tp-arrow next"; + nextArrow.innerHTML = "❯"; + nextArrow.addEventListener("click", () => this.next()); + + this.appendChild(prevArrow); + this.appendChild(nextArrow); + } +} + diff --git a/webpack.config.js b/webpack.config.js index a347deb..a15b627 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -78,6 +78,7 @@ module.exports = ( env ) => { entry: { modal: './src/modal/index.ts', slider: './src/slider/index.ts', + newSlider: './src/new-slider/index.ts', tabs: './src/tabs/index.ts', form: './src/form/index.ts', accordion: './src/accordion/index.ts', From 60440f2ad7465f3691850334f75c6ce574ca4016 Mon Sep 17 00:00:00 2001 From: Ashish Rawat Date: Tue, 18 Mar 2025 23:38:00 +0530 Subject: [PATCH 02/31] tp-new-slider update --- package.json | 3 +- src/new-slider/index.html | 53 ++- src/new-slider/index.ts | 18 +- src/new-slider/slider-components.ts | 146 +++++++ src/new-slider/style.scss | 196 +++++++--- src/new-slider/tp-slider.ts | 580 ++++++++++++++++++++++------ 6 files changed, 801 insertions(+), 195 deletions(-) create mode 100644 src/new-slider/slider-components.ts diff --git a/package.json b/package.json index d8d2afa..99b0026 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "scripts": { "build": "webpack --config ./webpack.config.js --mode production --env=production", "dev": "webpack --config ./webpack.config.js --mode production --env=development --watch", - "lint": "eslint src" + "lint": "eslint src", + "lint:fix": "eslint src --fix" }, "repository": { "type": "git", diff --git a/src/new-slider/index.html b/src/new-slider/index.html index 113157e..ae95b86 100644 --- a/src/new-slider/index.html +++ b/src/new-slider/index.html @@ -1,23 +1,48 @@ - + - - + + + TP Slider Example + + +

TP Slider Example

+ - Slide 1 - Slide 2 - Slide 3 - Slide 1 - Slide 2 - Slide 3 + + + + + + Slide 1 + Slide 2 + Slide 3 + Slide 4 + + + + + + + + + + + diff --git a/src/new-slider/index.ts b/src/new-slider/index.ts index 6b43961..dbf130c 100644 --- a/src/new-slider/index.ts +++ b/src/new-slider/index.ts @@ -1,14 +1,18 @@ /** - * Styles. + * index.ts - Main entry point for registering all components */ import './style.scss'; -/** - * Components. - */ +// Import all component classes import { TPSlider } from './tp-slider'; +import { TPSliderTrack, TPSliderSlides, TPSliderSlide, TPSliderArrow, TPSliderNav, TPSliderNavItem, TPSliderCount } from './slider-components'; -/** - * Register Components. - */ +// Register all custom elements customElements.define( 'tp-slider', TPSlider ); +customElements.define( 'tp-slider-track', TPSliderTrack ); +customElements.define( 'tp-slider-slides', TPSliderSlides ); +customElements.define( 'tp-slider-slide', TPSliderSlide ); +customElements.define( 'tp-slider-arrow', TPSliderArrow ); +customElements.define( 'tp-slider-nav', TPSliderNav ); +customElements.define( 'tp-slider-nav-item', TPSliderNavItem ); +customElements.define( 'tp-slider-count', TPSliderCount ); diff --git a/src/new-slider/slider-components.ts b/src/new-slider/slider-components.ts new file mode 100644 index 0000000..148b3ea --- /dev/null +++ b/src/new-slider/slider-components.ts @@ -0,0 +1,146 @@ +/** + * components/tp-slider-track.ts + * Container for the slider slides + */ +export class TPSliderTrack extends HTMLElement { + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-track' ); + } +} + +/** + * components/tp-slider-slides.ts + * Container for individual slides + */ +export class TPSliderSlides extends HTMLElement { + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-slides' ); + } +} + +/** + * components/tp-slider-slide.ts + * Individual slide component + */ +export class TPSliderSlide extends HTMLElement { + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-slide' ); + } +} + +/** + * components/tp-slider-arrow.ts + * Navigation arrow component + */ +export class TPSliderArrow extends HTMLElement { + get direction(): string { + return this.getAttribute( 'direction' ) || 'next'; + } + + // TODO: Add comment. + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-arrow' ); + this.classList.add( this.direction ); + } +} + +/** + * components/tp-slider-nav.ts + * Navigation dots container + */ +export class TPSliderNav extends HTMLElement { + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-nav' ); + } +} + +/** + * components/tp-slider-nav-item.ts + * Individual navigation dot + */ +export class TPSliderNavItem extends HTMLElement { + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-nav-item' ); + } +} + +/** + * components/tp-slider-count.ts + * Slide counter component + */ +export class TPSliderCount extends HTMLElement { + get current(): number { + return parseInt( this.getAttribute( 'current' ) || '1', 10 ); + } + + // TODO: Add comment. + get total(): number { + return parseInt( this.getAttribute( 'total' ) || '1', 10 ); + } + + // TODO: Add comment. + get format(): string { + return this.getAttribute( 'format' ) || '$current / $total'; + } + + // TODO: Add comment. + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + this.classList.add( 'tp-slider-count' ); + this.updateDisplay(); + } + + // TODO: Add comment. + static get observedAttributes() { + return [ 'current', 'total', 'format' ]; + } + + // TODO: Add comment. + attributeChangedCallback() { + this.updateDisplay(); + } + + // TODO: Add comment. + updateDisplay() { + const formattedText = this.format + .replace( '$current', this.current.toString() ) + .replace( '$total', this.total.toString() ); + + // TODO: Add comment. + this.textContent = formattedText; + } +} diff --git a/src/new-slider/style.scss b/src/new-slider/style.scss index 57c983c..e25c713 100644 --- a/src/new-slider/style.scss +++ b/src/new-slider/style.scss @@ -1,50 +1,150 @@ +/** + * style.scss + * Styles for the slider component + */ + +// Base slider styles tp-slider { - display: flex; - overflow-x: hidden; - scroll-snap-type: x mandatory; - position: relative; - gap: 15px; -} - -/* Arrows */ -.tp-arrow { - position: absolute; - top: 50%; - transform: translateY(-50%); - cursor: pointer; - z-index: 10; - background: rgba(0, 0, 0, 0.5); - padding: 10px; - border-radius: 50%; -} -.tp-arrow.prev { - left: 10px; -} -.tp-arrow.next { - right: 10px; -} - -/* Dots/Pagination */ -.tp-dots { - position: absolute; - bottom: 10px; - left: 50%; - transform: translateX(-50%); - display: flex; - gap: 8px; -} -.tp-dot { - width: 12px; - height: 12px; - border-radius: 50%; - background: #ccc; - cursor: pointer; -} -.tp-dot.active { - background: #007bff; -} - -/* Infinite Scroll Clones */ -.tp-slide.clone { - opacity: 0.5; + position: relative; + display: block; + width: 100%; + margin: 0 auto; + box-sizing: border-box; +} + +// Track container +.tp-slider-track { + position: relative; + overflow: hidden; + width: 100%; + + &.flexible-height { + height: auto; + } +} + +// Slides container +.tp-slider-slides { + display: flex; + width: 100%; + transition: transform 0.3s ease; + + &.tp-slide-behaviour { + display: flex; + flex-wrap: nowrap; + } + + &.tp-fade-behaviour { + position: relative; + } +} + +// Individual slide +.tp-slider-slide { + flex: 0 0 100%; + box-sizing: border-box; + padding: 0; + + img { + max-width: 100%; + height: auto; + display: block; + } + + .tp-fade-behaviour & { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s ease, visibility 0.3s ease; + + &.active { + opacity: 1; + visibility: visible; + } + } +} + +// Arrows +.tp-slider-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 10; + cursor: pointer; + + &.previous { + left: 10px; + } + + &.next { + right: 10px; + } + + button { + background: rgba(0, 0, 0, 0.5); + color: white; + border: none; + border-radius: 4px; + padding: 8px 12px; + cursor: pointer; + transition: background 0.3s ease; + + &:hover { + background: rgba(0, 0, 0, 0.7); + } + } +} + +// Navigation dots +.tp-slider-nav { + position: absolute; + bottom: 10px; + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 8px; + z-index: 10; +} + +.tp-slider-nav-item { + button { + width: 12px; + height: 12px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.5); + border: none; + cursor: pointer; + transition: background 0.3s ease; + padding: 0; + + &:hover { + background: rgba(255, 255, 255, 0.8); + } + } + + &.active button { + background: white; + } +} + +// Slide counter +.tp-slider-count { + position: absolute; + bottom: 10px; + right: 10px; + background: rgba(0, 0, 0, 0.5); + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 14px; + z-index: 10; +} + +// Cloned slides for infinite scroll +.tp-slider-slide.clone { + opacity: 1; } diff --git a/src/new-slider/tp-slider.ts b/src/new-slider/tp-slider.ts index fdaa3e6..44514b4 100644 --- a/src/new-slider/tp-slider.ts +++ b/src/new-slider/tp-slider.ts @@ -1,127 +1,457 @@ +/** + * components/tp-slider.ts - Main slider component that orchestrates the functionality + */ export class TPSlider extends HTMLElement { - private slides: HTMLElement[]; - private currentIndex: number; - private autoPlayInterval: number | null; - private slidesPerView: number; - private dotsContainer!: HTMLDivElement; - - constructor() { - super(); - this.slides = []; - this.currentIndex = 0; - this.autoPlayInterval = null; - this.slidesPerView = 1; - this.init(); - } - - // Initialize Component - private init(): void { - this.setupSlides(); - if (this.hasAttribute("arrows")) this.addArrows(); - if (this.hasAttribute("dots")) this.addDots(); - if (this.hasAttribute("infinite")) this.addClones(); - if (this.hasAttribute("auto-play")) this.startAutoPlay(); - } - - // Setup Slides & Scroll - private setupSlides(): void { - this.slides = [...this.querySelectorAll("tp-slide")] as HTMLElement[]; - this.slidesPerView = parseInt(this.getAttribute("slides-per-view") || "1"); - this.style.overflowX = "auto"; - this.slides.forEach((slide) => { - slide.style.flex = `0 0 calc(100% / ${this.slidesPerView} - 10px)`; - slide.style.scrollSnapAlign = "start"; - }); - } - - // Next/Previous Methods - public next(): void { - this.currentIndex = (this.currentIndex + 1) % this.slides.length; - this.scrollToSlide(this.currentIndex); - } - - public prev(): void { - this.currentIndex = - (this.currentIndex - 1 + this.slides.length) % this.slides.length; - this.scrollToSlide(this.currentIndex); - } - - // Scroll to Specific Slide - public scrollToSlide(index: number): void { - const slideWidth = this.slides[0].offsetWidth + 10; // Include gap - this.scrollTo({ - left: slideWidth * index, - behavior: "smooth", - }); - this.updateDots(index); - } - - // Pagination Dots - private addDots(): void { - this.dotsContainer = document.createElement("div"); - this.dotsContainer.className = "tp-dots"; - this.slides.forEach((_, i) => { - const dot = document.createElement("div"); - dot.className = `tp-dot ${i === 0 ? "active" : ""}`; - dot.addEventListener("click", () => this.scrollToSlide(i)); - this.dotsContainer.appendChild(dot); - }); - this.appendChild(this.dotsContainer); - } - - // Update Active Dot - private updateDots(activeIndex: number): void { - if (!this.dotsContainer) return; - - const dots = this.dotsContainer.querySelectorAll(".tp-dot"); - dots.forEach((dot, i) => { - dot.classList.toggle("active", i === activeIndex); - }); - } - - // Infinite Scroll (Clone First/Last Slides) - private addClones(): void { - const firstClone = this.slides[0].cloneNode(true) as HTMLElement; - const lastClone = this.slides[this.slides.length - 1].cloneNode(true) as HTMLElement; - firstClone.classList.add("clone"); - lastClone.classList.add("clone"); - this.append(firstClone); - this.prepend(lastClone); - } - - // Auto-Play - private startAutoPlay(): void { - const delay = parseInt(this.getAttribute("auto-play") || "3000"); - this.autoPlayInterval = window.setInterval(() => this.next(), delay); - - this.addEventListener("mouseenter", () => { - if (this.autoPlayInterval) { - clearInterval(this.autoPlayInterval); - this.autoPlayInterval = null; - } - }); - - this.addEventListener("mouseleave", () => { - if (!this.autoPlayInterval) { - this.startAutoPlay(); - } - }); - } - - // Arrows - private addArrows(): void { - const prevArrow = document.createElement("div"); - prevArrow.className = "tp-arrow prev"; - prevArrow.innerHTML = "❮"; - prevArrow.addEventListener("click", () => this.prev()); - - const nextArrow = document.createElement("div"); - nextArrow.className = "tp-arrow next"; - nextArrow.innerHTML = "❯"; - nextArrow.addEventListener("click", () => this.next()); - - this.appendChild(prevArrow); - this.appendChild(nextArrow); - } -} + // Properties + private currentIndex: number = 0; + private autoPlayInterval: number | null = null; + private touchStartX: number = 0; + private touchEndX: number = 0; + private responsiveSettings: any[] = []; + private behaviour: string = 'slide'; // 'slide' or 'fade' + + // Elements (will be populated in connectedCallback) + private track: HTMLElement | null = null; + private slidesContainer: HTMLElement | null = null; + private slides: HTMLElement[] = []; + private navItems: NodeListOf | null = null; + private countElement: HTMLElement | null = null; + private prevArrow: HTMLElement | null = null; + private nextArrow: HTMLElement | null = null; + + // Getters for attributes + get infiniteScroll(): boolean { + return this.hasAttribute( 'infinite' ) && this.getAttribute( 'infinite' ) !== 'no'; + } + + // TODO: Add comment. + get autoSlideInterval(): number { + return this.hasAttribute( 'auto-slide-interval' ) + ? parseInt( this.getAttribute( 'auto-slide-interval' ) || '0', 10 ) + : 0; + } + + // TODO: Add comment. + get perView(): number { + return this.hasAttribute( 'per-view' ) + ? parseInt( this.getAttribute( 'per-view' ) || '1', 10 ) + : 1; + } + + // TODO: Add comment. + get step(): number { + return this.hasAttribute( 'step' ) + ? parseInt( this.getAttribute( 'step' ) || '1', 10 ) + : 1; + } + + // TODO: Add comment. + get flexibleHeight(): boolean { + return this.hasAttribute( 'flexible-height' ) && this.getAttribute( 'flexible-height' ) !== 'no'; + } + + // TODO: Add comment. + get swipeEnabled(): boolean { + return this.hasAttribute( 'swipe' ) && this.getAttribute( 'swipe' ) !== 'no'; + } + + // TODO: Add comment. + get swipeThreshold(): number { + return this.hasAttribute( 'swipe-threshold' ) + ? parseInt( this.getAttribute( 'swipe-threshold' ) || '50', 10 ) + : 50; + } + + // TODO: Add comment. + constructor() { + super(); + } + + // TODO: Add comment. + connectedCallback() { + // Parse responsive settings first + this.parseResponsiveSettings(); + + // Get all the necessary elements + this.track = this.querySelector( 'tp-slider-track' ); + this.slidesContainer = this.querySelector( 'tp-slider-slides' ); + this.slides = Array.from( this.querySelectorAll( 'tp-slider-slide' ) ); + this.navItems = this.querySelectorAll( 'tp-slider-nav-item' ); + this.countElement = this.querySelector( 'tp-slider-count' ); + this.prevArrow = this.querySelector( 'tp-slider-arrow[direction="previous"]' ); + this.nextArrow = this.querySelector( 'tp-slider-arrow[direction="next"]' ); + + // Apply responsive settings + this.applyResponsiveSettings(); + + // Set up event listeners + this.setupEventListeners(); + + // Initialize slider state + this.updateSliderState(); + + // Start auto-sliding if enabled + this.setupAutoPlay(); + } + + // TODO: Add comment. + disconnectedCallback() { + // Clean up event listeners + this.removeEventListeners(); + + // Stop auto-play + if ( this.autoPlayInterval ) { + clearInterval( this.autoPlayInterval ); + this.autoPlayInterval = null; + } + } + + // TODO: Add comment. + static get observedAttributes() { + return [ + 'per-view', + 'step', + 'infinite', + 'auto-slide-interval', + 'behaviour', + 'flexible-height', + 'swipe', + 'swipe-threshold', + 'responsive', + ]; + } + + // TODO: Add comment. + attributeChangedCallback( name: string, oldValue: string, newValue: string ) { + if ( oldValue === newValue ) { + return; + } + + // TODO: Add comment. + switch ( name ) { + case 'responsive': + this.parseResponsiveSettings(); + this.applyResponsiveSettings(); + break; + case 'behaviour': + this.behaviour = newValue || 'slide'; + this.updateSliderState(); + break; + case 'per-view': + case 'step': + case 'infinite': + case 'flexible-height': + this.updateSliderState(); + break; + case 'auto-slide-interval': + this.setupAutoPlay(); + break; + case 'swipe': + this.updateSwipeHandlers(); + break; + } + } + + // Parse responsive settings from attribute + private parseResponsiveSettings(): void { + if ( this.hasAttribute( 'responsive' ) ) { + try { + this.responsiveSettings = JSON.parse( this.getAttribute( 'responsive' ) || '[]' ); + } catch ( e ) { + console.error( 'Invalid responsive settings JSON:', e ); + this.responsiveSettings = []; + } + } + } + + // Apply responsive settings based on current viewport + private applyResponsiveSettings(): void { + // Loop through responsive settings in reverse order (mobile-first approach) + for ( const setting of this.responsiveSettings ) { + if ( window.matchMedia( setting.media ).matches ) { + // Apply settings to attributes + Object.entries( setting ).forEach( ( [ key, value ] ) => { + if ( key !== 'media' ) { + this.setAttribute( key, String( value ) ); + } + } ); + break; // Apply only the first matching media query + } + } + } + + // Set up event listeners + private setupEventListeners(): void { + // Arrow navigation + if ( this.prevArrow ) { + this.prevArrow.addEventListener( 'click', () => this.prev() ); + } + + // TODO: Add comment. + if ( this.nextArrow ) { + this.nextArrow.addEventListener( 'click', () => this.next() ); + } + + // Nav item clicks + if ( this.navItems ) { + this.navItems.forEach( ( item, index ) => { + item.addEventListener( 'click', () => this.goToSlide( index ) ); + } ); + } + + // Swipe handlers + this.updateSwipeHandlers(); + + // Window resize handler + window.addEventListener( 'resize', this.handleResize.bind( this ) ); + } + + // Remove event listeners + private removeEventListeners(): void { + window.removeEventListener( 'resize', this.handleResize.bind( this ) ); + + // Remove swipe handlers + if ( this.slidesContainer ) { + this.slidesContainer.removeEventListener( 'touchstart', this.handleTouchStart.bind( this ) ); + this.slidesContainer.removeEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); + } + } + + // Handle window resize + private handleResize(): void { + this.applyResponsiveSettings(); + this.updateSliderState(); + } + + // Update swipe handlers + private updateSwipeHandlers(): void { + if ( ! this.slidesContainer ) { + return; + } + + // Remove existing listeners to avoid duplicates + this.slidesContainer.removeEventListener( 'touchstart', this.handleTouchStart.bind( this ) ); + this.slidesContainer.removeEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); + + // Add new listeners if swipe is enabled + if ( this.swipeEnabled ) { + this.slidesContainer.addEventListener( 'touchstart', this.handleTouchStart.bind( this ), { passive: true } ); + this.slidesContainer.addEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); + } + } + + // Handle touch start + private handleTouchStart( e: TouchEvent ): void { + this.touchStartX = e.changedTouches[ 0 ].screenX; + } + // Handle touch end + private handleTouchEnd( e: TouchEvent ): void { + this.touchEndX = e.changedTouches[ 0 ].screenX; + this.handleSwipe(); + } + + // Handle swipe gesture + private handleSwipe(): void { + const swipeDistance = this.touchEndX - this.touchStartX; + + // TODO: Add comment. + if ( Math.abs( swipeDistance ) < this.swipeThreshold ) { + return; + } + + // TODO: Add comment. + if ( swipeDistance < 0 ) { + // Swipe left - go next + this.next(); + } else { + // Swipe right - go previous + this.prev(); + } + } + + // Set up auto-play + private setupAutoPlay(): void { + // Clear existing interval + if ( this.autoPlayInterval ) { + clearInterval( this.autoPlayInterval ); + this.autoPlayInterval = null; + } + + // Set up new interval if needed + if ( this.autoSlideInterval > 0 ) { + this.autoPlayInterval = window.setInterval( () => this.next(), this.autoSlideInterval ); + + // Pause on hover + this.addEventListener( 'mouseenter', () => { + if ( this.autoPlayInterval ) { + clearInterval( this.autoPlayInterval ); + this.autoPlayInterval = null; + } + } ); + + // TODO: Add comment. + this.addEventListener( 'mouseleave', () => { + if ( ! this.autoPlayInterval && this.autoSlideInterval > 0 ) { + this.autoPlayInterval = window.setInterval( () => this.next(), this.autoSlideInterval ); + } + } ); + } + } + + // Update slider state based on current settings + private updateSliderState(): void { + if ( ! this.slidesContainer || this.slides.length === 0 ) { + return; + } + + // Update behaviour-specific styles + if ( this.behaviour === 'fade' ) { + // Fade behaviour + this.slidesContainer.classList.add( 'tp-fade-behaviour' ); + this.slidesContainer.classList.remove( 'tp-slide-behaviour' ); + + // Update slide visibility + this.slides.forEach( ( slide, index ) => { + slide.classList.toggle( 'active', index === this.currentIndex ); + } ); + } else { + // Slide behaviour + this.slidesContainer.classList.add( 'tp-slide-behaviour' ); + this.slidesContainer.classList.remove( 'tp-fade-behaviour' ); + + // Set slide width based on perView + this.slides.forEach( ( slide ) => { + slide.style.flex = `0 0 calc(100% / ${ this.perView })`; + } ); + + // Position the slides + this.scrollToSlide( this.currentIndex ); + } + + // Update flexible height + if ( this.flexibleHeight ) { + this.track?.classList.add( 'flexible-height' ); + } else { + this.track?.classList.remove( 'flexible-height' ); + } + + // Update navigation state + this.updateNavigation(); + + // Update count + this.updateCount(); + } + + // Go to next slide + public next(): void { + const newIndex = this.currentIndex + this.step; + + // TODO: Add comment. + if ( newIndex >= this.slides.length ) { + if ( this.infiniteScroll ) { + // Loop back to the beginning + this.goToSlide( 0 ); + } else { + // Stay at the last slide + this.goToSlide( this.slides.length - 1 ); + } + } else { + this.goToSlide( newIndex ); + } + } + + // Go to previous slide + public prev(): void { + const newIndex = this.currentIndex - this.step; + + // TODO: Add comment. + if ( newIndex < 0 ) { + if ( this.infiniteScroll ) { + // Loop to the end + this.goToSlide( this.slides.length - 1 ); + } else { + // Stay at the first slide + this.goToSlide( 0 ); + } + } else { + this.goToSlide( newIndex ); + } + } + + // Go to specific slide + public goToSlide( index: number ): void { + // Constrain index to valid range + const safeIndex = Math.max( 0, Math.min( index, this.slides.length - 1 ) ); + + // Update current index + this.currentIndex = safeIndex; + + // Update slides based on behaviour + if ( this.behaviour === 'fade' ) { + this.slides.forEach( ( slide, i ) => { + slide.classList.toggle( 'active', i === this.currentIndex ); + } ); + } else { + this.scrollToSlide( this.currentIndex ); + } + + // Update navigation + this.updateNavigation(); + + // Update count + this.updateCount(); + + // Dispatch event + this.dispatchEvent( new CustomEvent( 'slide-change', { + detail: { index: this.currentIndex }, + } ) ); + } + + // Scroll to specific slide (for slide behaviour) + private scrollToSlide( index: number ): void { + if ( ! this.slidesContainer || ! this.slides[ index ] ) { + return; + } + + // TODO: Add comment. + const slideWidth = this.slides[ 0 ].offsetWidth; + const offset = slideWidth * index; + + // TODO: Add comment. + this.slidesContainer.style.transform = `translateX(-${ offset }px)`; + } + + // Update navigation state + private updateNavigation(): void { + if ( this.navItems ) { + this.navItems.forEach( ( item, index ) => { + item.classList.toggle( 'active', index === this.currentIndex ); + } ); + } + } + + // Update count element + private updateCount(): void { + if ( ! this.countElement ) { + return; + } + + // TODO: Add comment. + const current = this.currentIndex + 1; + const total = this.slides.length; + + // Update attributes + this.countElement.setAttribute( 'current', current.toString() ); + this.countElement.setAttribute( 'total', total.toString() ); + + // Update content based on format + const format = this.countElement.getAttribute( 'format' ) || '$current / $total'; + const formattedCount = format + .replace( '$current', current.toString() ) + .replace( '$total', total.toString() ); + + // TODO: Add comment. + this.countElement.textContent = formattedCount; + } +} From 572076920c4cde745a00223cc13f4d37859ebed2 Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 5 Mar 2025 16:57:13 +1100 Subject: [PATCH 03/31] WP-96 tooltip first commit --- package-lock.json | 6 +++--- src/tooltip/index.html | 22 ++++++++++++++++++++++ src/tooltip/index.ts | 14 ++++++++++++++ src/tooltip/style.scss | 1 + src/tooltip/tp-tooltip.ts | 5 +++++ webpack.config.js | 1 + 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/tooltip/index.html create mode 100644 src/tooltip/index.ts create mode 100644 src/tooltip/style.scss create mode 100644 src/tooltip/tp-tooltip.ts diff --git a/package-lock.json b/package-lock.json index 69fd47d..94f2679 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3620,9 +3620,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001650", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001650.tgz", - "integrity": "sha512-fgEc7hP/LB7iicdXHUI9VsBsMZmUmlVJeQP2qqQW+3lkqVhbmjEU8zp+h5stWeilX+G7uXuIUIIlWlDw9jdt8g==", + "version": "1.0.30001702", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz", + "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==", "dev": true, "funding": [ { diff --git a/src/tooltip/index.html b/src/tooltip/index.html new file mode 100644 index 0000000..9f63d38 --- /dev/null +++ b/src/tooltip/index.html @@ -0,0 +1,22 @@ + + + + + + + + Web Component: Tooltip + + + + + + + + +
+ +
+ + diff --git a/src/tooltip/index.ts b/src/tooltip/index.ts new file mode 100644 index 0000000..1fd9437 --- /dev/null +++ b/src/tooltip/index.ts @@ -0,0 +1,14 @@ +/** + * Styles. + */ +import './style.scss'; + +/** + * Components. + */ +import { TPTooltip } from './tp-tooltip'; + +/** + * Register Components. + */ +customElements.define( 'tp-tooltip', TPTooltip ); diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss new file mode 100644 index 0000000..661bcbd --- /dev/null +++ b/src/tooltip/style.scss @@ -0,0 +1 @@ +tp-tooltip {} diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts new file mode 100644 index 0000000..6282f42 --- /dev/null +++ b/src/tooltip/tp-tooltip.ts @@ -0,0 +1,5 @@ +/** + * TP Tooltip. + */ +export class TPTooltip extends HTMLElement { +} diff --git a/webpack.config.js b/webpack.config.js index a15b627..37c1fa3 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -86,6 +86,7 @@ module.exports = ( env ) => { lightbox: './src/lightbox/index.ts', 'toggle-attribute': './src/toggle-attribute/index.ts', 'number-spinner': './src/number-spinner/index.ts', + tooltip: './src/tooltip/index.ts', }, module: { rules: [ From 436985e9cf8a882a8b863b2dcf6c45f02b05ab0e Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 5 Mar 2025 17:49:28 +1100 Subject: [PATCH 04/31] WP-96 add tooltip trigger --- src/tooltip/index.html | 48 ++++++++++++++++++- src/tooltip/index.ts | 2 + src/tooltip/style.scss | 8 +++- src/tooltip/tp-tooltip-trigger.ts | 77 +++++++++++++++++++++++++++++++ src/tooltip/tp-tooltip.ts | 60 ++++++++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/tooltip/tp-tooltip-trigger.ts diff --git a/src/tooltip/index.html b/src/tooltip/index.html index 9f63d38..f3a0dc7 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -16,7 +16,53 @@
- + + + + + + + + + + + + + + + +

+ Hi, this is a paragraph and here is: + + + trigger 1 + + and + + + trigger 2 + + and + + + trigger 3 + +

+

+ And just for fun, here's a button: + + + + +

diff --git a/src/tooltip/index.ts b/src/tooltip/index.ts index 1fd9437..1a90352 100644 --- a/src/tooltip/index.ts +++ b/src/tooltip/index.ts @@ -7,8 +7,10 @@ import './style.scss'; * Components. */ import { TPTooltip } from './tp-tooltip'; +import { TPTooltipTrigger } from './tp-tooltip-trigger'; /** * Register Components. */ customElements.define( 'tp-tooltip', TPTooltip ); +customElements.define( 'tp-tooltip-trigger', TPTooltipTrigger ); diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index 661bcbd..5c2d541 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -1 +1,7 @@ -tp-tooltip {} +tp-tooltip { + display: none; + + &[show] { + display: block; + } +} diff --git a/src/tooltip/tp-tooltip-trigger.ts b/src/tooltip/tp-tooltip-trigger.ts new file mode 100644 index 0000000..63cdbd9 --- /dev/null +++ b/src/tooltip/tp-tooltip-trigger.ts @@ -0,0 +1,77 @@ +/** + * Internal dependencies. + */ +import { TPTooltip } from './tp-tooltip'; + +/** + * TP Tooltip Trigger. + */ +export class TPTooltipTrigger extends HTMLElement { + /** + * Constructor. + */ + constructor() { + // Call parent's constructor. + super(); + + // Events. + this.addEventListener( 'mouseenter', this.showTooltip.bind( this ) ); + this.addEventListener( 'mouseleave', this.hideTooltip.bind( this ) ); + } + + /** + * Show the tooltip. + */ + showTooltip(): void { + // Get the tooltip. + const tooltipId: string = this.getAttribute( 'tooltip' ) ?? ''; + + // Check if we have a tooltip. + if ( '' === tooltipId ) { + // We don't, bail. + return; + } + + // Find and show the tooltip with its content. + const tooltip: TPTooltip | null = document.querySelector( `#${ tooltipId }` ); + tooltip?.setContent( this.getContent() ); + tooltip?.show(); + } + + /** + * Hide the tooltip. + */ + hideTooltip(): void { + // Get the tooltip. + const tooltipId: string = this.getAttribute( 'tooltip' ) ?? ''; + + // Check if we have a tooltip. + if ( '' === tooltipId ) { + // We don't, bail. + return; + } + + // Find and hide the tooltip. + const tooltip: TPTooltip | null = document.querySelector( `#${ tooltipId }` ); + tooltip?.hide(); + } + + /** + * Get the content of the tooltip. + * + * @return {Node|null} The content of the tooltip. + */ + getContent(): Node | null { + // Find template for content. + const template: HTMLTemplateElement | null = this.querySelector( 'template' ); + + // Check if we found a template. + if ( template ) { + // We did, return its content. + return template.content.cloneNode( true ); + } + + // No template found, return null. + return null; + } +} diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index 6282f42..f0d0bea 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -2,4 +2,64 @@ * TP Tooltip. */ export class TPTooltip extends HTMLElement { + /** + * Constructor. + */ + constructor() { + // Call parent's constructor. + super(); + + // Make the tooltip a popover. + this.makePopover(); + } + + /** + * Make this tooltip a popover, if it isn't already. + */ + makePopover(): void { + // Check if this isn't already a popover. + if ( ! this.getAttribute( 'popover' ) ) { + this.setAttribute( 'popover', 'popover' ); + } + } + + /** + * Set the content for our tooltip. + * + * @param {Node|null} content The content of the tooltip. + */ + setContent( content: Node|null ): void { + // Check if we have a valid content node. + if ( ! content ) { + // We don't, bail. + return; + } + + // Replace slot's children with new content. + this.querySelector( 'slot' )?.replaceChildren( content ); + } + + /** + * Set the position of the tooltip. + */ + setPosition(): void { + // Set the position of this tooltip. + } + + /** + * Show the tooltip. + */ + show(): void { + // Position tooltip and show it. + this.setPosition(); + this.setAttribute( 'show', 'yes' ); + } + + /** + * Hide the tooltip. + */ + hide(): void { + // Hide the attribute. + this.removeAttribute( 'show' ); + } } From 6f19220d9b4e9b68881c34add9861fbfd6dada1e Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 5 Mar 2025 18:13:57 +1100 Subject: [PATCH 05/31] WP-96 set the position of the tooltip --- src/tooltip/index.ts | 2 + src/tooltip/style.scss | 4 ++ src/tooltip/tp-tooltip-arrow.ts | 5 ++ src/tooltip/tp-tooltip-trigger.ts | 22 ++++++++- src/tooltip/tp-tooltip.ts | 81 ++++++++++++++++++++++++++----- 5 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 src/tooltip/tp-tooltip-arrow.ts diff --git a/src/tooltip/index.ts b/src/tooltip/index.ts index 1a90352..89552c7 100644 --- a/src/tooltip/index.ts +++ b/src/tooltip/index.ts @@ -8,9 +8,11 @@ import './style.scss'; */ import { TPTooltip } from './tp-tooltip'; import { TPTooltipTrigger } from './tp-tooltip-trigger'; +import { TPTooltipArrow } from './tp-tooltip-arrow'; /** * Register Components. */ customElements.define( 'tp-tooltip', TPTooltip ); customElements.define( 'tp-tooltip-trigger', TPTooltipTrigger ); +customElements.define( 'tp-tooltip-arrow', TPTooltipArrow ); diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index 5c2d541..d2c2970 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -1,6 +1,10 @@ tp-tooltip { display: none; + &[popover] { + margin: 0; + } + &[show] { display: block; } diff --git a/src/tooltip/tp-tooltip-arrow.ts b/src/tooltip/tp-tooltip-arrow.ts new file mode 100644 index 0000000..d9dcce4 --- /dev/null +++ b/src/tooltip/tp-tooltip-arrow.ts @@ -0,0 +1,5 @@ +/** + * TP Tooltip Arrow. + */ +export class TPTooltipArrow extends HTMLElement { +} diff --git a/src/tooltip/tp-tooltip-trigger.ts b/src/tooltip/tp-tooltip-trigger.ts index 63cdbd9..8f62518 100644 --- a/src/tooltip/tp-tooltip-trigger.ts +++ b/src/tooltip/tp-tooltip-trigger.ts @@ -19,6 +19,26 @@ export class TPTooltipTrigger extends HTMLElement { this.addEventListener( 'mouseleave', this.hideTooltip.bind( this ) ); } + /** + * Get offset. + */ + get offset(): number { + // Get the offset. + return parseInt( this.getAttribute( 'offset' ) ?? '0' ); + } + + /** + * Set offset. + */ + set offset( offset: number ) { + // Set or remove offset. + if ( ! offset ) { + this.removeAttribute( 'offset' ); + } else { + this.setAttribute( 'offset', offset.toString() ); + } + } + /** * Show the tooltip. */ @@ -34,7 +54,7 @@ export class TPTooltipTrigger extends HTMLElement { // Find and show the tooltip with its content. const tooltip: TPTooltip | null = document.querySelector( `#${ tooltipId }` ); - tooltip?.setContent( this.getContent() ); + tooltip?.setTrigger( this ); tooltip?.show(); } diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index f0d0bea..b8b9081 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -1,7 +1,18 @@ +/** + * Internal dependencies. + */ +import { TPTooltipTrigger } from './tp-tooltip-trigger'; +import { TPTooltipArrow } from './tp-tooltip-arrow'; + /** * TP Tooltip. */ export class TPTooltip extends HTMLElement { + /** + * Properties. + */ + protected trigger: TPTooltipTrigger | null = null; + /** * Constructor. */ @@ -24,26 +35,73 @@ export class TPTooltip extends HTMLElement { } /** - * Set the content for our tooltip. + * Set the trigger. * - * @param {Node|null} content The content of the tooltip. + * @param {HTMLElement} trigger The trigger node. */ - setContent( content: Node|null ): void { - // Check if we have a valid content node. - if ( ! content ) { - // We don't, bail. - return; - } + setTrigger( trigger: TPTooltipTrigger ): void { + // Set the trigger. + this.trigger = trigger; + } + + /** + * Set the content for our tooltip. + */ + setContent(): void { + // Get content. + const content: Node | null = this.trigger?.getContent() ?? null; - // Replace slot's children with new content. - this.querySelector( 'slot' )?.replaceChildren( content ); + // Check if we have content. + if ( content ) { + // Yes, replace slot's children with new content. + this.querySelector( 'slot' )?.replaceChildren( content ); + } } /** * Set the position of the tooltip. */ setPosition(): void { - // Set the position of this tooltip. + // Do we have a trigger? + if ( ! this.trigger ) { + // We don't, bail! + return; + } + + // Get width and height of this tooltip. + const { height: tooltipHeight, width: tooltipWidth } = this.getBoundingClientRect(); + + // Get position and coordinates of the trigger. + const { x: triggerLeftPosition, y: triggerTopPosition, width: triggerWidth, height: triggerHeight } = this.trigger.getBoundingClientRect(); + + // Get arrow dimensions. + let arrowHeight: number = 0; + const arrow: TPTooltipArrow | null = this.querySelector( 'tp-tooltop-arrow' ); + + // Check if we have an arrow. + if ( arrow ) { + ( { height: arrowHeight } = arrow.getBoundingClientRect() ); + } + + // Determine the vertical position of this tooltip. + if ( triggerTopPosition > tooltipHeight + this.trigger.offset + arrowHeight ) { + // There is enough space on top of trigger element, so place popover above the trigger element. + this.style.top = `${ triggerTopPosition - tooltipHeight - this.trigger.offset - ( arrowHeight / 2 ) }px`; + + // Set arrow placement on bottom of popover + arrow?.setAttribute( 'position', 'bottom' ); + } else { + // There is not enough space on top of trigger element, so place popover below the trigger element. + this.style.top = `${ triggerTopPosition + triggerHeight + this.trigger.offset + ( arrowHeight / 2 ) }px`; + + // Set arrow placement on top of popover + arrow?.setAttribute( 'position', 'top' ); + } + + // Determine the horizontal position of this tooltip. + if ( triggerLeftPosition + ( triggerWidth / 2 ) > ( tooltipWidth / 2 ) ) { + this.style.left = `${ triggerLeftPosition + ( triggerWidth / 2 ) - ( tooltipWidth / 2 ) }px`; + } } /** @@ -51,6 +109,7 @@ export class TPTooltip extends HTMLElement { */ show(): void { // Position tooltip and show it. + this.setContent(); this.setPosition(); this.setAttribute( 'show', 'yes' ); } From 55a36939be9b3be4b3ce6e002313ad9c8df05a58 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Tue, 11 Mar 2025 15:58:26 +0530 Subject: [PATCH 06/31] Add styling needed for arrow and minor changes --- src/tooltip/index.html | 32 +++++++++++++++++++++++++++++++- src/tooltip/style.scss | 30 +++++++++++++++++++++++++++--- src/tooltip/tp-tooltip.ts | 10 +++++++--- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index f3a0dc7..df945ed 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -11,6 +11,16 @@ @@ -31,6 +41,13 @@ + + + + + + +

Hi, this is a paragraph and here is: @@ -56,13 +73,26 @@

And just for fun, here's a button: - +

+ +

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, ratione non doloribus aspernatur totam dolor dignissimos aliquam rem, ipsum quod distinctio sint voluptas qui? Dolores quae voluptatibus ut facere cumque, ex quisquam. Ut magnam provident corrupti dolore ipsum ullam hic aliquid itaque eaque odit necessitatibus quidem ab quam earum dolor repellendus sequi, consequuntur fuga? Praesentium sed asperiores libero. Placeat provident ex dignissimos enim laudantium reprehenderit labore obcaecati consectetur numquam dolores mollitia sapiente ut exercitationem doloribus aliquid tenetur fugiat, architecto praesentium doloremque rem! Sequi, ipsam doloribus dolorum quaerat molestiae officia porro odit inventore a rerum vitae quidem, amet molestias eligendi expedita, labore libero explicabo autem architecto? Tempora reprehenderit, dolorem qui esse inventore, ratione omnis recusandae modi adipisci iusto et facere dolor soluta fuga cupiditate autem, minus corrupti at quis iste! Ipsam illum, iste assumenda aperiam quo eos aut voluptatem, accusamus sunt dignissimos, deleniti explicabo ab nemo dolores porro libero tempore asperiores recusandae distinctio in doloremque. Consequuntur ex inventore cumque facilis facere voluptate suscipit cupiditate molestias nulla amet porro incidunt autem fugiat laudantium tempora perferendis, doloremque esse, saepe quae illo a? Vitae pariatur atque sunt asperiores laboriosam repudiandae expedita modi obcaecati magnam culpa cumque ipsum eius impedit, repellendus omnis laudantium blanditiis molestias? Quasi dicta aliquid possimus impedit pariatur dolor repellat quo, consequuntur architecto temporibus labore quia quidem, assumenda nisi quis accusantium, quisquam voluptatum neque? Porro dolor dolorum quos voluptates quo aut asperiores similique placeat, quibusdam reiciendis! Ad, praesentium unde. Iure, hic blanditiis molestias ratione voluptas ipsam reprehenderit debitis, placeat tenetur ut tempora numquam, aperiam modi? Voluptatum, odit commodi dignissimos + + + + + + beatae ipsa vitae hic id reprehenderit cupiditate quisquam recusandae unde? Commodi, labore voluptatum recusandae exercitationem modi libero optio qui vitae? Laboriosam eveniet ullam in laborum debitis ex reiciendis aspernatur harum voluptates doloribus? Exercitationem ipsa quos fuga tempore numquam explicabo earum sed accusamus eos. Praesentium, aut maiores? Beatae quis ratione et repudiandae. Eligendi dolore quisquam velit quidem nulla, eveniet in facere debitis excepturi? Reiciendis, id. Optio, dolore suscipit ab id qui minus iusto placeat perspiciatis. Architecto rem cupiditate iusto esse dolores quisquam quae consequuntur aperiam nulla rerum? Laboriosam ipsum, impedit odit ut ex incidunt quod in! Veniam consectetur atque assumenda libero dicta doloribus exercitationem tempore adipisci iusto, eum expedita voluptatibus. Temporibus deserunt sed esse itaque ipsam officia atque? Maxime consectetur quibusdam omnis illum, debitis accusantium accusamus exercitationem expedita eum quia ipsa repellendus commodi deleniti consequatur vitae incidunt aperiam, nesciunt deserunt excepturi adipisci officia illo esse! Consectetur, ipsum sapiente adipisci dolor tenetur laudantium necessitatibus, omnis, non ea sequi iste nemo voluptatum voluptate nihil. Pariatur consequatur accusantium sunt ipsa quasi fugit, error ab assumenda aut rem fuga saepe maxime repellat commodi libero explicabo, voluptas doloremque tenetur asperiores laudantium a! Eius repudiandae optio illum ab quo? At ab doloribus ipsum aliquid itaque deleniti rem eius, vero hic illum, iusto laborum, officiis dolore distinctio cumque. Modi hic doloribus iusto quia explicabo, aspernatur, nulla ratione perspiciatis sint recusandae aliquam officia. Quibusdam libero perspiciatis molestias eveniet quo dolores iusto sed ex pariatur fugit obcaecati rerum maiores, consequatur modi numquam.

+ + + diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index d2c2970..e33bdfc 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -1,11 +1,35 @@ tp-tooltip { - display: none; - + position: relative; + z-index: 0; + &[popover] { + overflow: visible; margin: 0; + padding: 0; + } + + tp-tooltip-content { + position: relative; + display: block; + z-index: 10; } - &[show] { + tp-tooltip-arrow { + position: absolute; display: block; + left: 50%; + z-index: 5; + + &[position="top"] { + top: 0; + transform: translateY(-50%) translateX(-50%) rotate(45deg); + } + + &[position="bottom"] { + bottom: 0; + transform: translateY(50%) translateX(-50%) rotate(45deg); + } } } + + diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index b8b9081..70d5374 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -30,7 +30,7 @@ export class TPTooltip extends HTMLElement { makePopover(): void { // Check if this isn't already a popover. if ( ! this.getAttribute( 'popover' ) ) { - this.setAttribute( 'popover', 'popover' ); + this.setAttribute( 'popover', '' ); } } @@ -76,7 +76,7 @@ export class TPTooltip extends HTMLElement { // Get arrow dimensions. let arrowHeight: number = 0; - const arrow: TPTooltipArrow | null = this.querySelector( 'tp-tooltop-arrow' ); + const arrow: TPTooltipArrow | null = this.querySelector( 'tp-tooltip-arrow' ); // Check if we have an arrow. if ( arrow ) { @@ -110,6 +110,9 @@ export class TPTooltip extends HTMLElement { show(): void { // Position tooltip and show it. this.setContent(); + + // Show the tooltip. + this.showPopover(); this.setPosition(); this.setAttribute( 'show', 'yes' ); } @@ -118,7 +121,8 @@ export class TPTooltip extends HTMLElement { * Hide the tooltip. */ hide(): void { - // Hide the attribute. + // Hide the tooltip. + this.hidePopover(); this.removeAttribute( 'show' ); } } From 608e0734083203e44825cfac4a3fd75d84a369a7 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Wed, 12 Mar 2025 16:16:18 +0530 Subject: [PATCH 07/31] Add more content to html for debugging edge cases --- src/tooltip/index.html | 219 +++++++++++++++++++++++++++----------- src/tooltip/tp-tooltip.ts | 6 +- 2 files changed, 163 insertions(+), 62 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index df945ed..b137891 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -27,70 +27,167 @@
- - - - - - +
+ + + + + + +

Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero excepturi, esse aperiam omnis voluptatem + et perspiciatis quas nostrum. Modi velit repellat labore illo, laudantium eaque, ea esse tempora earum + natus ullam repudiandae voluptatibus ut perferendis nobis. Nesciunt, possimus accusamus sed dolor + exercitationem repellendus sunt perspiciatis. Nulla saepe assumenda facere. Aliquid et eum accusantium + velit nisi rem, eaque, repudiandae voluptas culpa dolor hic at aperiam dignissimos voluptatum quia, + architecto incidunt! Unde ipsa maiores maxime repellendus, voluptatem assumenda doloribus fugit + necessitatibus ipsam soluta laboriosam voluptas optio a tenetur eveniet nostrum debitis hic, beatae + eos asperiores repellat consectetur animi est dolor? Quis, recusandae neque deserunt asperiores culpa + + + Para link 1 + + exercitationem autem consequatur, sequi saepe blanditiis, laudantium vel? Voluptates pariatur ducimus + corporis magni. Placeat, fugiat, veniam incidunt dolorum numquam itaque non inventore, aliquam laboriosam + dolor mollitia ipsa! Nemo voluptas, animi corrupti facere perspiciatis commodi voluptatibus sint saepe + suscipit? Molestiae temporibus recusandae delectus molestias, ea, tempora unde, sapiente quae reiciendis + suscipit est? Nemo enim eius suscipit possimus eum debitis adipisci officiis, dolor, iste autem delectus + quam illum ut asperiores expedita impedit ad atque repellat aut quisquam doloremque! Nihil magnam harum + + + Para link 2 + + dolores in neque perspiciatis! Velit, repellendus sapiente cumque ullam optio molestias ipsum doloribus + blanditiis. Eum at quibusdam voluptatem fugiat facere omnis ipsam est obcaecati provident laborum dolores + exercitationem aspernatur saepe cumque animi eligendi corrupti, aliquid tenetur. Similique quam accusantium + numquam a, aliquam libero debitis quibusdam consequatur modi doloremque, exercitationem eum temporibus quo + quia velit repellendus, aspernatur eos. Optio exercitationem dolore ipsum alias est quas velit eaque autem + ducim us. Iusto fugit reiciendis nisi fugiat deserunt, quibusdam doloribus. Eligendi a officia quas totam + adipisci minus voluptatibus dignissimos sed nostrum. Consequatur eos est illo tempore odit, adipisci itaque + + + Para link 3 + + magnam, obcaecati ullam possimus ab sint beatae, animi sed mollitia sit perspiciatis eligendi! Tenetur minus + vero soluta amet excepturi sit quia animi. +

+
- - - - - - +
- - - - - - - -

- Hi, this is a paragraph and here is: - - - trigger 1 - - and - - - trigger 2 - - and - - - trigger 3 - -

-

- And just for fun, here's a button: - - - - -

- -

Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, ratione non doloribus aspernatur totam dolor dignissimos aliquam rem, ipsum quod distinctio sint voluptas qui? Dolores quae voluptatibus ut facere cumque, ex quisquam. Ut magnam provident corrupti dolore ipsum ullam hic aliquid itaque eaque odit necessitatibus quidem ab quam earum dolor repellendus sequi, consequuntur fuga? Praesentium sed asperiores libero. Placeat provident ex dignissimos enim laudantium reprehenderit labore obcaecati consectetur numquam dolores mollitia sapiente ut exercitationem doloribus aliquid tenetur fugiat, architecto praesentium doloremque rem! Sequi, ipsam doloribus dolorum quaerat molestiae officia porro odit inventore a rerum vitae quidem, amet molestias eligendi expedita, labore libero explicabo autem architecto? Tempora reprehenderit, dolorem qui esse inventore, ratione omnis recusandae modi adipisci iusto et facere dolor soluta fuga cupiditate autem, minus corrupti at quis iste! Ipsam illum, iste assumenda aperiam quo eos aut voluptatem, accusamus sunt dignissimos, deleniti explicabo ab nemo dolores porro libero tempore asperiores recusandae distinctio in doloremque. Consequuntur ex inventore cumque facilis facere voluptate suscipit cupiditate molestias nulla amet porro incidunt autem fugiat laudantium tempora perferendis, doloremque esse, saepe quae illo a? Vitae pariatur atque sunt asperiores laboriosam repudiandae expedita modi obcaecati magnam culpa cumque ipsum eius impedit, repellendus omnis laudantium blanditiis molestias? Quasi dicta aliquid possimus impedit pariatur dolor repellat quo, consequuntur architecto temporibus labore quia quidem, assumenda nisi quis accusantium, quisquam voluptatum neque? Porro dolor dolorum quos voluptates quo aut asperiores similique placeat, quibusdam reiciendis! Ad, praesentium unde. Iure, hic blanditiis molestias ratione voluptas ipsam reprehenderit debitis, placeat tenetur ut tempora numquam, aperiam modi? Voluptatum, odit commodi dignissimos - - - - - - beatae ipsa vitae hic id reprehenderit cupiditate quisquam recusandae unde? Commodi, labore voluptatum recusandae exercitationem modi libero optio qui vitae? Laboriosam eveniet ullam in laborum debitis ex reiciendis aspernatur harum voluptates doloribus? Exercitationem ipsa quos fuga tempore numquam explicabo earum sed accusamus eos. Praesentium, aut maiores? Beatae quis ratione et repudiandae. Eligendi dolore quisquam velit quidem nulla, eveniet in facere debitis excepturi? Reiciendis, id. Optio, dolore suscipit ab id qui minus iusto placeat perspiciatis. Architecto rem cupiditate iusto esse dolores quisquam quae consequuntur aperiam nulla rerum? Laboriosam ipsum, impedit odit ut ex incidunt quod in! Veniam consectetur atque assumenda libero dicta doloribus exercitationem tempore adipisci iusto, eum expedita voluptatibus. Temporibus deserunt sed esse itaque ipsam officia atque? Maxime consectetur quibusdam omnis illum, debitis accusantium accusamus exercitationem expedita eum quia ipsa repellendus commodi deleniti consequatur vitae incidunt aperiam, nesciunt deserunt excepturi adipisci officia illo esse! Consectetur, ipsum sapiente adipisci dolor tenetur laudantium necessitatibus, omnis, non ea sequi iste nemo voluptatum voluptate nihil. Pariatur consequatur accusantium sunt ipsa quasi fugit, error ab assumenda aut rem fuga saepe maxime repellat commodi libero explicabo, voluptas doloremque tenetur asperiores laudantium a! Eius repudiandae optio illum ab quo? At ab doloribus ipsum aliquid itaque deleniti rem eius, vero hic illum, iusto laborum, officiis dolore distinctio cumque. Modi hic doloribus iusto quia explicabo, aspernatur, nulla ratione perspiciatis sint recusandae aliquam officia. Quibusdam libero perspiciatis molestias eveniet quo dolores iusto sed ex pariatur fugit obcaecati rerum maiores, consequatur modi numquam.

+
+ + + + + + +
+
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+ +
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+
+ Card Image + + +
+

Card Title

+ i +
+
+

This is a simple description for the card. It provides some basic information about the content displayed above.

+
+
+
diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index 70d5374..614cb01 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -70,9 +70,13 @@ export class TPTooltip extends HTMLElement { // Get width and height of this tooltip. const { height: tooltipHeight, width: tooltipWidth } = this.getBoundingClientRect(); + console.log( 'tooltipHeight', tooltipHeight, 'tooltipWidth', tooltipWidth ); + // Get position and coordinates of the trigger. const { x: triggerLeftPosition, y: triggerTopPosition, width: triggerWidth, height: triggerHeight } = this.trigger.getBoundingClientRect(); + console.log( 'triggerLeftPosition', triggerLeftPosition, 'triggerTopPosition', triggerTopPosition, 'triggerWidth', triggerWidth, 'triggerHeight', triggerHeight ); + // Get arrow dimensions. let arrowHeight: number = 0; @@ -86,7 +90,7 @@ export class TPTooltip extends HTMLElement { // Determine the vertical position of this tooltip. if ( triggerTopPosition > tooltipHeight + this.trigger.offset + arrowHeight ) { // There is enough space on top of trigger element, so place popover above the trigger element. - this.style.top = `${ triggerTopPosition - tooltipHeight - this.trigger.offset - ( arrowHeight / 2 ) }px`; + this.style.marginTop = `${ triggerTopPosition - tooltipHeight - this.trigger.offset - ( arrowHeight / 2 ) }px`; // Set arrow placement on bottom of popover arrow?.setAttribute( 'position', 'bottom' ); From 098dddce67651a9d6667d2c4f270b6de48befe79 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Wed, 19 Mar 2025 09:47:15 +0530 Subject: [PATCH 08/31] WP-96 Add arrow styles and position code --- src/tooltip/index.html | 12 +++++------- src/tooltip/style.scss | 2 -- src/tooltip/tp-tooltip-trigger.ts | 20 ------------------- src/tooltip/tp-tooltip.ts | 32 +++++++++++++++++++++++-------- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index b137891..ebe9bd8 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -28,11 +28,11 @@
- + - +

Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero excepturi, esse aperiam omnis voluptatem et perspiciatis quas nostrum. Modi velit repellat labore illo, laudantium eaque, ea esse tempora earum @@ -87,11 +87,11 @@

- + - +
@@ -108,8 +108,7 @@

Card Title

This is a simple description for the card. It provides some basic information about the content displayed above.

- - +
Card Image @@ -185,7 +184,6 @@

Card Title

This is a simple description for the card. It provides some basic information about the content displayed above.

-
diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index e33bdfc..f534fa2 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -1,6 +1,4 @@ tp-tooltip { - position: relative; - z-index: 0; &[popover] { overflow: visible; diff --git a/src/tooltip/tp-tooltip-trigger.ts b/src/tooltip/tp-tooltip-trigger.ts index 8f62518..cca74f0 100644 --- a/src/tooltip/tp-tooltip-trigger.ts +++ b/src/tooltip/tp-tooltip-trigger.ts @@ -19,26 +19,6 @@ export class TPTooltipTrigger extends HTMLElement { this.addEventListener( 'mouseleave', this.hideTooltip.bind( this ) ); } - /** - * Get offset. - */ - get offset(): number { - // Get the offset. - return parseInt( this.getAttribute( 'offset' ) ?? '0' ); - } - - /** - * Set offset. - */ - set offset( offset: number ) { - // Set or remove offset. - if ( ! offset ) { - this.removeAttribute( 'offset' ); - } else { - this.setAttribute( 'offset', offset.toString() ); - } - } - /** * Show the tooltip. */ diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index 614cb01..88520f0 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -24,6 +24,26 @@ export class TPTooltip extends HTMLElement { this.makePopover(); } + /** + * Get offset. + */ + get offset(): number { + // Get the offset. + return parseInt( this.getAttribute( 'offset' ) ?? '0' ); + } + + /** + * Set offset. + */ + set offset( offset: number ) { + // Set or remove offset. + if ( ! offset ) { + this.removeAttribute( 'offset' ); + } else { + this.setAttribute( 'offset', offset.toString() ); + } + } + /** * Make this tooltip a popover, if it isn't already. */ @@ -70,13 +90,9 @@ export class TPTooltip extends HTMLElement { // Get width and height of this tooltip. const { height: tooltipHeight, width: tooltipWidth } = this.getBoundingClientRect(); - console.log( 'tooltipHeight', tooltipHeight, 'tooltipWidth', tooltipWidth ); - // Get position and coordinates of the trigger. const { x: triggerLeftPosition, y: triggerTopPosition, width: triggerWidth, height: triggerHeight } = this.trigger.getBoundingClientRect(); - console.log( 'triggerLeftPosition', triggerLeftPosition, 'triggerTopPosition', triggerTopPosition, 'triggerWidth', triggerWidth, 'triggerHeight', triggerHeight ); - // Get arrow dimensions. let arrowHeight: number = 0; @@ -88,15 +104,15 @@ export class TPTooltip extends HTMLElement { } // Determine the vertical position of this tooltip. - if ( triggerTopPosition > tooltipHeight + this.trigger.offset + arrowHeight ) { + if ( triggerTopPosition > tooltipHeight + this.offset + arrowHeight ) { // There is enough space on top of trigger element, so place popover above the trigger element. - this.style.marginTop = `${ triggerTopPosition - tooltipHeight - this.trigger.offset - ( arrowHeight / 2 ) }px`; + this.style.marginTop = `${ triggerTopPosition - tooltipHeight - this.offset - ( arrowHeight / 2 ) }px`; // Set arrow placement on bottom of popover arrow?.setAttribute( 'position', 'bottom' ); } else { // There is not enough space on top of trigger element, so place popover below the trigger element. - this.style.top = `${ triggerTopPosition + triggerHeight + this.trigger.offset + ( arrowHeight / 2 ) }px`; + this.style.marginTop = `${ triggerTopPosition + triggerHeight + this.offset + ( arrowHeight / 2 ) }px`; // Set arrow placement on top of popover arrow?.setAttribute( 'position', 'top' ); @@ -104,7 +120,7 @@ export class TPTooltip extends HTMLElement { // Determine the horizontal position of this tooltip. if ( triggerLeftPosition + ( triggerWidth / 2 ) > ( tooltipWidth / 2 ) ) { - this.style.left = `${ triggerLeftPosition + ( triggerWidth / 2 ) - ( tooltipWidth / 2 ) }px`; + this.style.marginLeft = `${ triggerLeftPosition + ( triggerWidth / 2 ) - ( tooltipWidth / 2 ) }px`; } } From a3143f0d61d616bcb58cc9e950f5a67a5580e914 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Wed, 19 Mar 2025 16:15:54 +0530 Subject: [PATCH 09/31] WP-96 Add window scroll listener and touch devices support --- src/tooltip/index.html | 2 +- src/tooltip/style.scss | 1 - src/tooltip/tp-tooltip-trigger.ts | 62 +++++++++++++++++++++++++++++-- src/tooltip/tp-tooltip.ts | 23 +++++++++++- 4 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index ebe9bd8..8a516e1 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -91,7 +91,7 @@ - +
diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index f534fa2..207c12e 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -15,7 +15,6 @@ tp-tooltip { tp-tooltip-arrow { position: absolute; display: block; - left: 50%; z-index: 5; &[position="top"] { diff --git a/src/tooltip/tp-tooltip-trigger.ts b/src/tooltip/tp-tooltip-trigger.ts index cca74f0..8a9ae58 100644 --- a/src/tooltip/tp-tooltip-trigger.ts +++ b/src/tooltip/tp-tooltip-trigger.ts @@ -14,9 +14,45 @@ export class TPTooltipTrigger extends HTMLElement { // Call parent's constructor. super(); - // Events. - this.addEventListener( 'mouseenter', this.showTooltip.bind( this ) ); - this.addEventListener( 'mouseleave', this.hideTooltip.bind( this ) ); + // Check if touch device. + if ( navigator.maxTouchPoints > 0 ) { + // Yes it is, toggle tooltip on click. + this.addEventListener( 'click', this.toggleTooltip.bind( this ) ); + } else { + // Else add event listeners for mouse. + this.addEventListener( 'mouseenter', this.showTooltip.bind( this ) ); + this.addEventListener( 'mouseleave', this.hideTooltip.bind( this ) ); + } + + // On window scroll, hide tooltip. + window.addEventListener( 'scroll', this.handleWindowScroll.bind( this ), true ); + } + + /** + * Toggle the tooltip. + */ + toggleTooltip(): void { + // Get the tooltip. + const tooltipId: string = this.getAttribute( 'tooltip' ) ?? ''; + + // Check if we have a tooltip. + if ( '' === tooltipId ) { + // We don't, bail. + return; + } + + // Find the tooltip. + const tooltip: TPTooltip | null = document.querySelector( `#${ tooltipId }` ); + + // Check if the tooltip is already shown. + if ( 'yes' === tooltip?.getAttribute( 'show' ) ) { + // It is, hide it. + tooltip?.hide(); + } else { + // It isn't, show it. + tooltip?.setTrigger( this ); + tooltip?.show(); + } } /** @@ -74,4 +110,24 @@ export class TPTooltipTrigger extends HTMLElement { // No template found, return null. return null; } + + /** + * Handles the scroll outside of the tooltip. + * + * @param { Event } evt The event object. + */ + handleWindowScroll( evt: Event ) { + // Get the tooltip. + const tooltipId: string = this.getAttribute( 'tooltip' ) ?? ''; + const tooltip: TPTooltip | null = document.querySelector( `#${ tooltipId }` ); + + // If the content was the original target. + if ( evt.target === tooltip ) { + // Do nothing. + return; + } + + // Hide the popover + this.hideTooltip(); + } } diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index 88520f0..7a9b7c4 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -94,6 +94,9 @@ export class TPTooltip extends HTMLElement { // Get position and coordinates of the trigger. const { x: triggerLeftPosition, y: triggerTopPosition, width: triggerWidth, height: triggerHeight } = this.trigger.getBoundingClientRect(); + // Trigger center from left edge of screen. + const triggerCenterPosition = triggerLeftPosition + ( triggerWidth / 2 ); + // Get arrow dimensions. let arrowHeight: number = 0; const arrow: TPTooltipArrow | null = this.querySelector( 'tp-tooltip-arrow' ); @@ -119,9 +122,27 @@ export class TPTooltip extends HTMLElement { } // Determine the horizontal position of this tooltip. - if ( triggerLeftPosition + ( triggerWidth / 2 ) > ( tooltipWidth / 2 ) ) { + if ( triggerCenterPosition < ( tooltipWidth / 2 ) ) { + // There is not enough space on left of trigger element, so place popover at the left edge of the screen. + this.style.marginLeft = '0px'; + } else if ( window.innerWidth - triggerCenterPosition < ( tooltipWidth / 2 ) ) { + // There is not enough space on right of trigger element, so place popover at the right edge of the screen. + this.style.marginLeft = `${ window.innerWidth - tooltipWidth }px`; + } else { + // There is enough space on both sides of trigger element, so place popover at the center of the trigger element. this.style.marginLeft = `${ triggerLeftPosition + ( triggerWidth / 2 ) - ( tooltipWidth / 2 ) }px`; } + + // Get left position of the tooltip. + const { left: tooltipLeftPosition } = this.getBoundingClientRect(); + + // Percentage the arrow should be moved from left edge of the tooltip. + const arrowPosition = ( ( triggerCenterPosition - tooltipLeftPosition ) / tooltipWidth ) * 100; + + // Set the arrow position. + if ( arrow ) { + arrow.style.left = `${ arrowPosition }%`; + } } /** From d39b21ad0b326a367f0a4b4f0983dfc1b4f0b03e Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Wed, 19 Mar 2025 21:55:21 +0530 Subject: [PATCH 10/31] WP-96 Remove redundant code --- src/tooltip/index.html | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index 8a516e1..28f100d 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -9,19 +9,6 @@ - - From c7f0137b3b25fc0af7e0e69c1c277e0f62fc246d Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Fri, 28 Mar 2025 10:10:28 +1100 Subject: [PATCH 11/31] WP-96 move styles --- src/tooltip/index.html | 53 +++++++++++++++++++++--------------------- src/tooltip/style.scss | 19 +++++++++++---- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/tooltip/index.html b/src/tooltip/index.html index 28f100d..b56b5f6 100644 --- a/src/tooltip/index.html +++ b/src/tooltip/index.html @@ -19,44 +19,44 @@ - + -

Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero excepturi, esse aperiam omnis voluptatem - et perspiciatis quas nostrum. Modi velit repellat labore illo, laudantium eaque, ea esse tempora earum - natus ullam repudiandae voluptatibus ut perferendis nobis. Nesciunt, possimus accusamus sed dolor +

Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero excepturi, esse aperiam omnis voluptatem + et perspiciatis quas nostrum. Modi velit repellat labore illo, laudantium eaque, ea esse tempora earum + natus ullam repudiandae voluptatibus ut perferendis nobis. Nesciunt, possimus accusamus sed dolor exercitationem repellendus sunt perspiciatis. Nulla saepe assumenda facere. Aliquid et eum accusantium - velit nisi rem, eaque, repudiandae voluptas culpa dolor hic at aperiam dignissimos voluptatum quia, - architecto incidunt! Unde ipsa maiores maxime repellendus, voluptatem assumenda doloribus fugit - necessitatibus ipsam soluta laboriosam voluptas optio a tenetur eveniet nostrum debitis hic, beatae - eos asperiores repellat consectetur animi est dolor? Quis, recusandae neque deserunt asperiores culpa + velit nisi rem, eaque, repudiandae voluptas culpa dolor hic at aperiam dignissimos voluptatum quia, + architecto incidunt! Unde ipsa maiores maxime repellendus, voluptatem assumenda doloribus fugit + necessitatibus ipsam soluta laboriosam voluptas optio a tenetur eveniet nostrum debitis hic, beatae + eos asperiores repellat consectetur animi est dolor? Quis, recusandae neque deserunt asperiores culpa - Para link 1 + Paragraph link 1 - exercitationem autem consequatur, sequi saepe blanditiis, laudantium vel? Voluptates pariatur ducimus - corporis magni. Placeat, fugiat, veniam incidunt dolorum numquam itaque non inventore, aliquam laboriosam - dolor mollitia ipsa! Nemo voluptas, animi corrupti facere perspiciatis commodi voluptatibus sint saepe - suscipit? Molestiae temporibus recusandae delectus molestias, ea, tempora unde, sapiente quae reiciendis - suscipit est? Nemo enim eius suscipit possimus eum debitis adipisci officiis, dolor, iste autem delectus - quam illum ut asperiores expedita impedit ad atque repellat aut quisquam doloremque! Nihil magnam harum + exercitationem autem consequatur, sequi saepe blanditiis, laudantium vel? Voluptates pariatur ducimus + corporis magni. Placeat, fugiat, veniam incidunt dolorum numquam itaque non inventore, aliquam laboriosam + dolor mollitia ipsa! Nemo voluptas, animi corrupti facere perspiciatis commodi voluptatibus sint saepe + suscipit? Molestiae temporibus recusandae delectus molestias, ea, tempora unde, sapiente quae reiciendis + suscipit est? Nemo enim eius suscipit possimus eum debitis adipisci officiis, dolor, iste autem delectus + quam illum ut asperiores expedita impedit ad atque repellat aut quisquam doloremque! Nihil magnam harum - Para link 2 + Paragraph link 2 - dolores in neque perspiciatis! Velit, repellendus sapiente cumque ullam optio molestias ipsum doloribus - blanditiis. Eum at quibusdam voluptatem fugiat facere omnis ipsam est obcaecati provident laborum dolores - exercitationem aspernatur saepe cumque animi eligendi corrupti, aliquid tenetur. Similique quam accusantium - numquam a, aliquam libero debitis quibusdam consequatur modi doloremque, exercitationem eum temporibus quo - quia velit repellendus, aspernatur eos. Optio exercitationem dolore ipsum alias est quas velit eaque autem - ducim us. Iusto fugit reiciendis nisi fugiat deserunt, quibusdam doloribus. Eligendi a officia quas totam + dolores in neque perspiciatis! Velit, repellendus sapiente cumque ullam optio molestias ipsum doloribus + blanditiis. Eum at quibusdam voluptatem fugiat facere omnis ipsam est obcaecati provident laborum dolores + exercitationem aspernatur saepe cumque animi eligendi corrupti, aliquid tenetur. Similique quam accusantium + numquam a, aliquam libero debitis quibusdam consequatur modi doloremque, exercitationem eum temporibus quo + quia velit repellendus, aspernatur eos. Optio exercitationem dolore ipsum alias est quas velit eaque autem + ducim us. Iusto fugit reiciendis nisi fugiat deserunt, quibusdam doloribus. Eligendi a officia quas totam adipisci minus voluptatibus dignissimos sed nostrum. Consequatur eos est illo tempore odit, adipisci itaque - Para link 3 + Paragraph link 3 - magnam, obcaecati ullam possimus ab sint beatae, animi sed mollitia sit perspiciatis eligendi! Tenetur minus + magnam, obcaecati ullam possimus ab sint beatae, animi sed mollitia sit perspiciatis eligendi! Tenetur minus vero soluta amet excepturi sit quia animi.

@@ -78,7 +78,7 @@ - +
@@ -95,7 +95,7 @@

Card Title

This is a simple description for the card. It provides some basic information about the content displayed above.

- +
Card Image @@ -174,7 +174,6 @@

Card Title

-
diff --git a/src/tooltip/style.scss b/src/tooltip/style.scss index 207c12e..f6e4b09 100644 --- a/src/tooltip/style.scss +++ b/src/tooltip/style.scss @@ -1,5 +1,6 @@ tp-tooltip { - + border: 1px solid #000; + &[popover] { overflow: visible; margin: 0; @@ -16,15 +17,23 @@ tp-tooltip { position: absolute; display: block; z-index: 5; - + border: 1px solid #000; + width: 15px; + height: 15px; + background-color: #fff; + &[position="top"] { - top: 0; + top: -1px; transform: translateY(-50%) translateX(-50%) rotate(45deg); + border-right: none; + border-bottom: none; } - + &[position="bottom"] { - bottom: 0; + bottom: -1px; transform: translateY(50%) translateX(-50%) rotate(45deg); + border-left: none; + border-top: none; } } } From 0bb75e59e6cd06a75fd4cccc6b8aa3ebc99918cc Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Fri, 28 Mar 2025 10:26:35 +1100 Subject: [PATCH 12/31] WP-96 add events and readme --- src/tooltip/README.md | 64 +++++++++++++++++++++++++++++++++++++++ src/tooltip/tp-tooltip.ts | 6 ++++ 2 files changed, 70 insertions(+) create mode 100644 src/tooltip/README.md diff --git a/src/tooltip/README.md b/src/tooltip/README.md new file mode 100644 index 0000000..13630b1 --- /dev/null +++ b/src/tooltip/README.md @@ -0,0 +1,64 @@ +# Tooltip + + + + + + +
+

Built by the super talented team at Travelopia.

+
+ +
+ +## Sample Usage + +This is a highly customizable tooltip component. + +Example: + +```js +// Import the component as needed: +import '@travelopia/web-components/dist/tooltip'; +import '@travelopia/web-components/dist/tooltip/style.css'; + +// No JavaScript is required to initialise! +``` + +```html + <-- Define and style the tooltip, and give it an ID + + <-- This is where the content of the tooltip would go + + <-- If you want an arrow + + +

+ Here is some informative content about + + <-- Make any element a tooltip trigger by wrapping this component + + interesting subject <-- The first direct descendant (that is not a template) is the trigger + + + that you may be interested in! +

+``` + +## Attributes + +| Attribute | Required | Values | Notes | +|---------------------|----------|----------|-----------------------------------------------------------------------------------------------------------------| +| offset | No | | The offset in pixels from the trigger that the tooltip should display | + +## Events + +| Event | Notes | +|-------|-------------------------------| +| show | After the tooltip is visible | +| hide | After the tooltip is hidden | diff --git a/src/tooltip/tp-tooltip.ts b/src/tooltip/tp-tooltip.ts index 7a9b7c4..9b9b2d7 100644 --- a/src/tooltip/tp-tooltip.ts +++ b/src/tooltip/tp-tooltip.ts @@ -156,6 +156,9 @@ export class TPTooltip extends HTMLElement { this.showPopover(); this.setPosition(); this.setAttribute( 'show', 'yes' ); + + // Trigger event. + this.dispatchEvent( new CustomEvent( 'show' ) ); } /** @@ -165,5 +168,8 @@ export class TPTooltip extends HTMLElement { // Hide the tooltip. this.hidePopover(); this.removeAttribute( 'show' ); + + // Trigger event. + this.dispatchEvent( new CustomEvent( 'hide' ) ); } } From dd9e8f1a4465fc1b79c6d60024a04404881f9006 Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Fri, 28 Mar 2025 10:30:45 +1100 Subject: [PATCH 13/31] bump version --- package-lock.json | 177 ++++++++++------------------------------------ package.json | 2 +- 2 files changed, 37 insertions(+), 142 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94f2679..046e8d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@travelopia/web-components", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@travelopia/web-components", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "devDependencies": { "@travelopia/eslint-plugin-wordpress-coding-standards": "^1.0.0", @@ -38,13 +38,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -382,9 +383,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "peer": true, @@ -393,9 +394,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -430,45 +431,29 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1977,9 +1962,9 @@ "peer": true }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dev": true, "license": "MIT", "peer": true, @@ -1991,16 +1976,16 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -2027,16 +2012,15 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -3172,19 +3156,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -3640,21 +3611,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -3717,23 +3673,6 @@ "node": ">=6" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -4360,16 +4299,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -5750,16 +5679,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -8443,19 +8362,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -8576,17 +8482,6 @@ "license": "MIT", "peer": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 99b0026..c5bece9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@travelopia/web-components", - "version": "0.8.0", + "version": "0.9.0", "description": "Accessible web components for the modern web", "files": [ "dist" From 347cc2e6ea745e09b49ddd7a813acecc86f882c0 Mon Sep 17 00:00:00 2001 From: monikatravelopia Date: Fri, 21 Mar 2025 11:41:50 +0530 Subject: [PATCH 14/31] Fix navigation issue --- src/slider/tp-slider-nav-item.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slider/tp-slider-nav-item.ts b/src/slider/tp-slider-nav-item.ts index e6db419..9dc01d1 100644 --- a/src/slider/tp-slider-nav-item.ts +++ b/src/slider/tp-slider-nav-item.ts @@ -53,9 +53,8 @@ export class TPSliderNavItemElement extends HTMLElement { // No, find it in the navigation. const slideNav: TPSliderNavElement | null = this.closest( 'tp-slider-nav' ); - const step = this.slider?.step; // Return index of this element considering the step value. - return ( Array.from( slideNav?.children ?? [] ).indexOf( this ) * ( step ?? 1 ) ) + 1; + return ( Array.from( slideNav?.children ?? [] ).indexOf( this ) + 1 ); } } From 7e88fed61a4212b1c4106ba06bee4e7fc652baa1 Mon Sep 17 00:00:00 2001 From: monikatravelopia Date: Fri, 28 Mar 2025 12:31:37 +0530 Subject: [PATCH 15/31] WP-183 Dynamic navigation for TP slider using template --- src/slider/index.html | 66 +++++++++++++++++++++++-------------- src/slider/tp-slider-nav.ts | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 24 deletions(-) diff --git a/src/slider/index.html b/src/slider/index.html index c3a68e2..3ec8787 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -51,10 +51,12 @@ - - - - + 1 / 4 @@ -80,10 +82,12 @@ - - - - + 1 / 4 @@ -109,10 +113,12 @@ - - - - + 1 / 4 @@ -131,9 +137,12 @@ - - - + 1 / 3 @@ -153,9 +162,12 @@ - - - + 1 / 3 @@ -185,8 +197,12 @@ - - + 1 / 4 @@ -212,10 +228,12 @@ - - - - + 1 / 4 diff --git a/src/slider/tp-slider-nav.ts b/src/slider/tp-slider-nav.ts index 5aae6c0..39f76ef 100644 --- a/src/slider/tp-slider-nav.ts +++ b/src/slider/tp-slider-nav.ts @@ -1,5 +1,69 @@ +/** + * TP Slider Nav. + */ + +/** + * Internal dependencies. + */ +import { TPSliderElement } from './tp-slider'; + /** * TP Slider Nav. */ export class TPSliderNavElement extends HTMLElement { + /** + * Properties. + */ + protected template: HTMLTemplateElement | null = null; + protected slider : TPSliderElement | null = null; + + /** + * Constructor. + */ + constructor() { + // Initialize parent. + super(); + + // Initialize properties. + this.template = this.querySelector( 'template' ); + this.slider = this.closest('tp-slider'); + + // Apply template if slider is found + this.setTemplate(); + + // Add event listener. + this.slider?.addEventListener( 'template-set', this.setTemplate.bind( this ) ); + } + + /** + * Set the template. + */ + setTemplate(): void { + // Bail if no template. + if ( ! this.template || ! this.slider ) { + // Exit. + return; + } + + // Total slides. + const totalSlides = Number( this.slider?.getAttribute( 'total' ) ?? 0 ); + + // Clear the navigation. + this.innerHTML = ''; + + // Append the navigation items. + for ( let i = 1; i <= totalSlides; i++ ) { + // Clone the template. + const navItem = this.template.content.cloneNode( true ) as HTMLTemplateElement; + + // Find the button inside and set its text. + const button = navItem.querySelector('button'); + if ( button ) { + button.textContent = `${i}`; // Add slide number in button + } + + // Append the navigation item. + this.appendChild( navItem ); + } + } } From 7723e46f2b925e6644f25e5369d593ef466d32f6 Mon Sep 17 00:00:00 2001 From: monikatravelopia Date: Fri, 28 Mar 2025 12:35:57 +0530 Subject: [PATCH 16/31] Revert navigation fix --- src/slider/tp-slider-nav-item.ts | 3 ++- src/slider/tp-slider-nav.ts | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/slider/tp-slider-nav-item.ts b/src/slider/tp-slider-nav-item.ts index 9dc01d1..e6db419 100644 --- a/src/slider/tp-slider-nav-item.ts +++ b/src/slider/tp-slider-nav-item.ts @@ -53,8 +53,9 @@ export class TPSliderNavItemElement extends HTMLElement { // No, find it in the navigation. const slideNav: TPSliderNavElement | null = this.closest( 'tp-slider-nav' ); + const step = this.slider?.step; // Return index of this element considering the step value. - return ( Array.from( slideNav?.children ?? [] ).indexOf( this ) + 1 ); + return ( Array.from( slideNav?.children ?? [] ).indexOf( this ) * ( step ?? 1 ) ) + 1; } } diff --git a/src/slider/tp-slider-nav.ts b/src/slider/tp-slider-nav.ts index 39f76ef..0e8b059 100644 --- a/src/slider/tp-slider-nav.ts +++ b/src/slider/tp-slider-nav.ts @@ -1,7 +1,3 @@ -/** - * TP Slider Nav. - */ - /** * Internal dependencies. */ @@ -26,7 +22,7 @@ export class TPSliderNavElement extends HTMLElement { // Initialize properties. this.template = this.querySelector( 'template' ); - this.slider = this.closest('tp-slider'); + this.slider = this.closest('tp-slider'); // Apply template if slider is found this.setTemplate(); From e09ff1543ecb0e07d6cf7dd10a82123408abf060 Mon Sep 17 00:00:00 2001 From: monikatravelopia Date: Tue, 1 Apr 2025 15:25:15 +0530 Subject: [PATCH 17/31] WP-183 Add nav based on step --- src/slider/tp-slider-nav.ts | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/slider/tp-slider-nav.ts b/src/slider/tp-slider-nav.ts index 0e8b059..0d8e1cf 100644 --- a/src/slider/tp-slider-nav.ts +++ b/src/slider/tp-slider-nav.ts @@ -20,21 +20,18 @@ export class TPSliderNavElement extends HTMLElement { // Initialize parent. super(); - // Initialize properties. - this.template = this.querySelector( 'template' ); - this.slider = this.closest('tp-slider'); - // Apply template if slider is found this.setTemplate(); - - // Add event listener. - this.slider?.addEventListener( 'template-set', this.setTemplate.bind( this ) ); } /** * Set the template. */ setTemplate(): void { + // Initialize properties. + this.template = this.querySelector( 'template' ); + this.slider = this.closest('tp-slider'); + // Bail if no template. if ( ! this.template || ! this.slider ) { // Exit. @@ -42,22 +39,20 @@ export class TPSliderNavElement extends HTMLElement { } // Total slides. + const step = Number( this.slider?.getAttribute( 'step' ) ? Number( this.slider?.getAttribute( 'per-view' ) ?? '1' ) : 1 ); const totalSlides = Number( this.slider?.getAttribute( 'total' ) ?? 0 ); + // Calculate the number of navigation items. + const totalNavItems = Math.ceil( totalSlides / step ); + // Clear the navigation. this.innerHTML = ''; // Append the navigation items. - for ( let i = 1; i <= totalSlides; i++ ) { + for ( let i = 1; i <= totalNavItems; i++ ) { // Clone the template. const navItem = this.template.content.cloneNode( true ) as HTMLTemplateElement; - // Find the button inside and set its text. - const button = navItem.querySelector('button'); - if ( button ) { - button.textContent = `${i}`; // Add slide number in button - } - // Append the navigation item. this.appendChild( navItem ); } From 30e10b4f58316d2dd09546737e061fe4256a24f5 Mon Sep 17 00:00:00 2001 From: monikatravelopia Date: Tue, 1 Apr 2025 15:30:10 +0530 Subject: [PATCH 18/31] WP-183 Remove multiple spaces --- src/slider/tp-slider-nav.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slider/tp-slider-nav.ts b/src/slider/tp-slider-nav.ts index 0d8e1cf..3e0a863 100644 --- a/src/slider/tp-slider-nav.ts +++ b/src/slider/tp-slider-nav.ts @@ -30,7 +30,7 @@ export class TPSliderNavElement extends HTMLElement { setTemplate(): void { // Initialize properties. this.template = this.querySelector( 'template' ); - this.slider = this.closest('tp-slider'); + this.slider = this.closest( 'tp-slider' ); // Bail if no template. if ( ! this.template || ! this.slider ) { @@ -39,7 +39,7 @@ export class TPSliderNavElement extends HTMLElement { } // Total slides. - const step = Number( this.slider?.getAttribute( 'step' ) ? Number( this.slider?.getAttribute( 'per-view' ) ?? '1' ) : 1 ); + const step = Number( this.slider?.getAttribute( 'step' ) ? Number( this.slider?.getAttribute( 'per-view' ) ?? '1' ) : 1 ); const totalSlides = Number( this.slider?.getAttribute( 'total' ) ?? 0 ); // Calculate the number of navigation items. From 372e120dc3416681cf76c6b39e1f9e88b1519d89 Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 2 Apr 2025 15:45:40 +1100 Subject: [PATCH 19/31] WP-183 update dynamic nav items --- src/slider/index.ts | 2 +- src/slider/tp-slider-nav.ts | 21 ++++++++++----------- src/slider/tp-slider.ts | 9 ++++++++- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/slider/index.ts b/src/slider/index.ts index 87f62f7..5eb6cc7 100644 --- a/src/slider/index.ts +++ b/src/slider/index.ts @@ -18,11 +18,11 @@ import { TPSliderCountElement } from './tp-slider-count'; /** * Register Components. */ +customElements.define( 'tp-slider-nav', TPSliderNavElement ); customElements.define( 'tp-slider', TPSliderElement ); customElements.define( 'tp-slider-count', TPSliderCountElement ); customElements.define( 'tp-slider-track', TPSliderTrackElement ); customElements.define( 'tp-slider-slides', TPSliderSlidesElement ); customElements.define( 'tp-slider-slide', TPSliderSlideElement ); customElements.define( 'tp-slider-arrow', TPSliderArrowElement ); -customElements.define( 'tp-slider-nav', TPSliderNavElement ); customElements.define( 'tp-slider-nav-item', TPSliderNavItemElement ); diff --git a/src/slider/tp-slider-nav.ts b/src/slider/tp-slider-nav.ts index 3e0a863..407de51 100644 --- a/src/slider/tp-slider-nav.ts +++ b/src/slider/tp-slider-nav.ts @@ -11,7 +11,7 @@ export class TPSliderNavElement extends HTMLElement { * Properties. */ protected template: HTMLTemplateElement | null = null; - protected slider : TPSliderElement | null = null; + protected slider: TPSliderElement | null = null; /** * Constructor. @@ -20,18 +20,15 @@ export class TPSliderNavElement extends HTMLElement { // Initialize parent. super(); - // Apply template if slider is found - this.setTemplate(); + // Get elements. + this.template = this.querySelector( 'template' ); + this.slider = this.closest( 'tp-slider' ); } /** - * Set the template. + * Update nav items based on template. */ - setTemplate(): void { - // Initialize properties. - this.template = this.querySelector( 'template' ); - this.slider = this.closest( 'tp-slider' ); - + public updateNavItems(): void { // Bail if no template. if ( ! this.template || ! this.slider ) { // Exit. @@ -51,10 +48,12 @@ export class TPSliderNavElement extends HTMLElement { // Append the navigation items. for ( let i = 1; i <= totalNavItems; i++ ) { // Clone the template. - const navItem = this.template.content.cloneNode( true ) as HTMLTemplateElement; + const navItem: Node = this.template.content.cloneNode( true ); + const div: HTMLDivElement = document.createElement( 'div' ); + div.appendChild( navItem ); // Append the navigation item. - this.appendChild( navItem ); + this.innerHTML += div.innerHTML.replace( '$index', i.toString() ); } } } diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index b1b476a..9c5409b 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -4,6 +4,7 @@ import { TPSliderSlidesElement } from './tp-slider-slides'; import { TPSliderSlideElement } from './tp-slider-slide'; import { TPSliderCountElement } from './tp-slider-count'; +import { TPSliderNavElement } from './tp-slider-nav'; import { TPSliderNavItemElement } from './tp-slider-nav-item'; import { TPSliderArrowElement } from './tp-slider-arrow'; @@ -367,7 +368,7 @@ export class TPSliderElement extends HTMLElement { */ update(): void { // Get sub-components. - const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); + const sliderNav: TPSliderNavElement | null = this.querySelector( 'tp-slider-nav' ); const sliderCounts: NodeListOf | null = this.querySelectorAll( 'tp-slider-count' ); const leftArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="previous"]' ); const rightArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="next"]' ); @@ -387,6 +388,12 @@ export class TPSliderElement extends HTMLElement { } ); } + // First, set the template for the slider nav. + sliderNav?.updateNavItems(); + + // Once the template has been set, query the slider nav items. + const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); + // Set current slider nav item. if ( sliderNavItems ) { sliderNavItems.forEach( ( navItem: TPSliderNavItemElement, index: number ): void => { From 8ae859f39db333d89cf715aad33fbb2a6b888a22 Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 2 Apr 2025 15:48:11 +1100 Subject: [PATCH 20/31] WP-183 update index file --- src/slider/index.html | 397 ++++++++++++++++++++---------------------- 1 file changed, 190 insertions(+), 207 deletions(-) diff --git a/src/slider/index.html b/src/slider/index.html index 3ec8787..ab1587c 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -31,212 +31,195 @@ -
- - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - 1 / 4 -
- -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - 1 / 4 -
- -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - 1 / 4 -
- -
- - - - - - - - - - - - - - - - 1 / 3 - - -
- - - - - - - - - - - - - - - - - 1 / 3 - - - - - - - - - - - - - - - - - - - - - - 1 / 4 - - - - - - - - 1 / 4 - - -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - 1 / 4 -
-
+
+ + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + 1 / 4 +
+ +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+ +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+ +
+ + + + + + + + + + + + + + + + + + 1 / 3 + + +
+ + + + + + + + + + + + + + + + + + + 1 / 3 + + + + + + + + + + + + + + + + + + + + + + 1 / 4 + + + + + + + + + 1 / 4 + + +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+
From 6bac9694d337b6ec2180e635b83c8dde80420fab Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Wed, 2 Apr 2025 15:49:43 +1100 Subject: [PATCH 21/31] WP-183 update index file --- src/slider/index.html | 380 +++++++++++++++++++++--------------------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/src/slider/index.html b/src/slider/index.html index ab1587c..c53dc8a 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -31,195 +31,195 @@ -
- - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - 1 / 4 -
- -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - - - - 1 / 4 -
- -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - - - - 1 / 4 -
- -
- - - - - - - - - - - - - - - - - - 1 / 3 - - -
- - - - - - - - - - - - - - - - - - - 1 / 3 - - - - - - - - - - - - - - - - - - - - - - 1 / 4 - - - - - - - - - 1 / 4 - - -
- - - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
-
-
- - - - - - - 1 / 4 -
-
+
+ + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + 1 / 4 +
+ +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+ +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+ +
+ + + + + + + + + + + + + + + + + + 1 / 3 + + +
+ + + + + + + + + + + + + + + + + + + 1 / 3 + + + + + + + + + + + + + + + + + + + + + + 1 / 4 + + + + + + + + + 1 / 4 + + +
+ + + + + + + + + + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+ + + + + + + 1 / 4 +
+
From f080522df155696b96b94320e15804e9916153eb Mon Sep 17 00:00:00 2001 From: roshniahuja Date: Wed, 2 Apr 2025 19:10:54 +0530 Subject: [PATCH 22/31] Added Set time out for update height function. Signed-off-by: roshniahuja --- src/slider/tp-slider.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 9c5409b..70b08fb 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -319,7 +319,10 @@ export class TPSliderElement extends HTMLElement { } // First, update the height. - this.updateHeight(); + setTimeout( () => { + // Update Height. + this.updateHeight(); + }, 0 ); // Now lets slide! const behaviour: string = this.getAttribute( 'behaviour' ) || ''; From b81bf1c3078fdf2e40028b91a33e9e3f3ab6b0ef Mon Sep 17 00:00:00 2001 From: roshniahuja Date: Thu, 3 Apr 2025 12:58:09 +0530 Subject: [PATCH 23/31] Changed set timeout structure. Signed-off-by: roshniahuja --- src/slider/tp-slider.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 70b08fb..6fa304f 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -319,10 +319,8 @@ export class TPSliderElement extends HTMLElement { } // First, update the height. - setTimeout( () => { - // Update Height. - this.updateHeight(); - }, 0 ); + // Yield to main thread to fix a bug in Safari 16. + setTimeout( this.updateHeight.bind( this ), 10 ); // Now lets slide! const behaviour: string = this.getAttribute( 'behaviour' ) || ''; From 5cc3fa3e42bea44ce4009630c11e9634c6656102 Mon Sep 17 00:00:00 2001 From: roshniahuja Date: Fri, 4 Apr 2025 12:43:21 +0530 Subject: [PATCH 24/31] Changed structure of set timeout. Signed-off-by: roshniahuja --- src/slider/tp-slider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 6fa304f..530fa2c 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -319,8 +319,9 @@ export class TPSliderElement extends HTMLElement { } // First, update the height. + // Yield to main thread to fix a bug in Safari 16. - setTimeout( this.updateHeight.bind( this ), 10 ); + setTimeout( () => this.updateHeight(), 0 ); // Now lets slide! const behaviour: string = this.getAttribute( 'behaviour' ) || ''; From 0ced494c046d47f030f7bec4f401b772a5d90b5b Mon Sep 17 00:00:00 2001 From: Junaid Bhura Date: Mon, 7 Apr 2025 08:27:23 +1000 Subject: [PATCH 25/31] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 046e8d9..17914c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@travelopia/web-components", - "version": "0.9.0", + "version": "0.9.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@travelopia/web-components", - "version": "0.9.0", + "version": "0.9.1", "license": "MIT", "devDependencies": { "@travelopia/eslint-plugin-wordpress-coding-standards": "^1.0.0", diff --git a/package.json b/package.json index c5bece9..ef65c73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@travelopia/web-components", - "version": "0.9.0", + "version": "0.9.1", "description": "Accessible web components for the modern web", "files": [ "dist" From 2581e1e75eb987172377c0c58b43a808a9e6dd78 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Sat, 26 Apr 2025 09:50:30 +0530 Subject: [PATCH 26/31] Add first version of working slider with snap --- src/new-slider/index.html | 82 ++-- src/new-slider/index.ts | 30 +- src/new-slider/slider-components.ts | 146 ------ src/new-slider/style.scss | 149 +----- src/new-slider/tp-slider-arrow.ts | 53 +++ src/new-slider/tp-slider-slide.ts | 20 + src/new-slider/tp-slider-slides.ts | 20 + src/new-slider/tp-slider-track.ts | 20 + src/new-slider/tp-slider.ts | 698 ++++++++++++---------------- 9 files changed, 484 insertions(+), 734 deletions(-) create mode 100644 src/new-slider/tp-slider-arrow.ts create mode 100644 src/new-slider/tp-slider-slide.ts create mode 100644 src/new-slider/tp-slider-slides.ts create mode 100644 src/new-slider/tp-slider-track.ts diff --git a/src/new-slider/index.html b/src/new-slider/index.html index ae95b86..0932a72 100644 --- a/src/new-slider/index.html +++ b/src/new-slider/index.html @@ -1,48 +1,46 @@ - + - - - TP Slider Example - - + + + + Web Component: Slider + + + + + -

TP Slider Example

- - - - - - - - Slide 1 - Slide 2 - Slide 3 - Slide 4 - - - - - - - - - - - - +
+ + + + + + + + + + + + + + +
diff --git a/src/new-slider/index.ts b/src/new-slider/index.ts index dbf130c..f6fc444 100644 --- a/src/new-slider/index.ts +++ b/src/new-slider/index.ts @@ -1,18 +1,22 @@ /** - * index.ts - Main entry point for registering all components + * Styles. */ import './style.scss'; -// Import all component classes -import { TPSlider } from './tp-slider'; -import { TPSliderTrack, TPSliderSlides, TPSliderSlide, TPSliderArrow, TPSliderNav, TPSliderNavItem, TPSliderCount } from './slider-components'; +/** + * Components. + */ +import { TPSliderElement } from './tp-slider'; +import { TPSliderTrackElement } from './tp-slider-track'; +import { TPSliderSlidesElement } from './tp-slider-slides'; +import { TPSliderSlideElement } from './tp-slider-slide'; +import { TPSliderArrowElement } from './tp-slider-arrow'; -// Register all custom elements -customElements.define( 'tp-slider', TPSlider ); -customElements.define( 'tp-slider-track', TPSliderTrack ); -customElements.define( 'tp-slider-slides', TPSliderSlides ); -customElements.define( 'tp-slider-slide', TPSliderSlide ); -customElements.define( 'tp-slider-arrow', TPSliderArrow ); -customElements.define( 'tp-slider-nav', TPSliderNav ); -customElements.define( 'tp-slider-nav-item', TPSliderNavItem ); -customElements.define( 'tp-slider-count', TPSliderCount ); +/** + * Register Components. + */ +customElements.define( 'tp-slider', TPSliderElement ); +customElements.define( 'tp-slider-track', TPSliderTrackElement ); +customElements.define( 'tp-slider-slides', TPSliderSlidesElement ); +customElements.define( 'tp-slider-slide', TPSliderSlideElement ); +customElements.define( 'tp-slider-arrow', TPSliderArrowElement ); diff --git a/src/new-slider/slider-components.ts b/src/new-slider/slider-components.ts index 148b3ea..e69de29 100644 --- a/src/new-slider/slider-components.ts +++ b/src/new-slider/slider-components.ts @@ -1,146 +0,0 @@ -/** - * components/tp-slider-track.ts - * Container for the slider slides - */ -export class TPSliderTrack extends HTMLElement { - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-track' ); - } -} - -/** - * components/tp-slider-slides.ts - * Container for individual slides - */ -export class TPSliderSlides extends HTMLElement { - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-slides' ); - } -} - -/** - * components/tp-slider-slide.ts - * Individual slide component - */ -export class TPSliderSlide extends HTMLElement { - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-slide' ); - } -} - -/** - * components/tp-slider-arrow.ts - * Navigation arrow component - */ -export class TPSliderArrow extends HTMLElement { - get direction(): string { - return this.getAttribute( 'direction' ) || 'next'; - } - - // TODO: Add comment. - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-arrow' ); - this.classList.add( this.direction ); - } -} - -/** - * components/tp-slider-nav.ts - * Navigation dots container - */ -export class TPSliderNav extends HTMLElement { - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-nav' ); - } -} - -/** - * components/tp-slider-nav-item.ts - * Individual navigation dot - */ -export class TPSliderNavItem extends HTMLElement { - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-nav-item' ); - } -} - -/** - * components/tp-slider-count.ts - * Slide counter component - */ -export class TPSliderCount extends HTMLElement { - get current(): number { - return parseInt( this.getAttribute( 'current' ) || '1', 10 ); - } - - // TODO: Add comment. - get total(): number { - return parseInt( this.getAttribute( 'total' ) || '1', 10 ); - } - - // TODO: Add comment. - get format(): string { - return this.getAttribute( 'format' ) || '$current / $total'; - } - - // TODO: Add comment. - constructor() { - super(); - } - - // TODO: Add comment. - connectedCallback() { - this.classList.add( 'tp-slider-count' ); - this.updateDisplay(); - } - - // TODO: Add comment. - static get observedAttributes() { - return [ 'current', 'total', 'format' ]; - } - - // TODO: Add comment. - attributeChangedCallback() { - this.updateDisplay(); - } - - // TODO: Add comment. - updateDisplay() { - const formattedText = this.format - .replace( '$current', this.current.toString() ) - .replace( '$total', this.total.toString() ); - - // TODO: Add comment. - this.textContent = formattedText; - } -} diff --git a/src/new-slider/style.scss b/src/new-slider/style.scss index e25c713..fbc3bda 100644 --- a/src/new-slider/style.scss +++ b/src/new-slider/style.scss @@ -1,150 +1,13 @@ -/** - * style.scss - * Styles for the slider component - */ -// Base slider styles -tp-slider { - position: relative; - display: block; - width: 100%; - margin: 0 auto; - box-sizing: border-box; -} - -// Track container -.tp-slider-track { - position: relative; - overflow: hidden; - width: 100%; - - &.flexible-height { - height: auto; - } -} -// Slides container -.tp-slider-slides { +tp-slider-slides { + overflow-y: visible; + overflow-x: auto; + scroll-snap-type: x mandatory; display: flex; - width: 100%; - transition: transform 0.3s ease; - - &.tp-slide-behaviour { - display: flex; - flex-wrap: nowrap; - } - - &.tp-fade-behaviour { - position: relative; - } } -// Individual slide -.tp-slider-slide { +tp-slider-slide { flex: 0 0 100%; - box-sizing: border-box; - padding: 0; - - img { - max-width: 100%; - height: auto; - display: block; - } - - .tp-fade-behaviour & { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0; - visibility: hidden; - transition: opacity 0.3s ease, visibility 0.3s ease; - - &.active { - opacity: 1; - visibility: visible; - } - } -} - -// Arrows -.tp-slider-arrow { - position: absolute; - top: 50%; - transform: translateY(-50%); - z-index: 10; - cursor: pointer; - - &.previous { - left: 10px; - } - - &.next { - right: 10px; - } - - button { - background: rgba(0, 0, 0, 0.5); - color: white; - border: none; - border-radius: 4px; - padding: 8px 12px; - cursor: pointer; - transition: background 0.3s ease; - - &:hover { - background: rgba(0, 0, 0, 0.7); - } - } -} - -// Navigation dots -.tp-slider-nav { - position: absolute; - bottom: 10px; - left: 50%; - transform: translateX(-50%); - display: flex; - gap: 8px; - z-index: 10; -} - -.tp-slider-nav-item { - button { - width: 12px; - height: 12px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.5); - border: none; - cursor: pointer; - transition: background 0.3s ease; - padding: 0; - - &:hover { - background: rgba(255, 255, 255, 0.8); - } - } - - &.active button { - background: white; - } -} - -// Slide counter -.tp-slider-count { - position: absolute; - bottom: 10px; - right: 10px; - background: rgba(0, 0, 0, 0.5); - color: white; - padding: 5px 10px; - border-radius: 4px; - font-size: 14px; - z-index: 10; -} - -// Cloned slides for infinite scroll -.tp-slider-slide.clone { - opacity: 1; + scroll-snap-align: start; } diff --git a/src/new-slider/tp-slider-arrow.ts b/src/new-slider/tp-slider-arrow.ts new file mode 100644 index 0000000..88afa09 --- /dev/null +++ b/src/new-slider/tp-slider-arrow.ts @@ -0,0 +1,53 @@ +/** + * Internal dependencies. + */ + +import { TPSliderElement } from "./tp-slider"; + +/** + * TP Slider Arrow. + */ +export class TPSliderArrowElement extends HTMLElement { + + + /** + * Constructor. + */ + constructor() { + + super(); + + // Get the button and add event listener. + this.querySelector( 'button' )?.addEventListener( 'click', this.handleClick.bind( this ) ); + + } + + /** + * Handle when the button is clicked. + */ + handleClick(): void { + // If disabled, do nothing. + if ( 'yes' === this.getAttribute( 'disabled' ) ) { + // Early return. + return; + } + + // Get the slider. + const slider: TPSliderElement | null = this.closest( 'tp-slider' ); + + // If no slider, early return. + if ( ! slider ) { + // Early return. + return; + } + + // Initiate slider according to the direction of the button clicked. + if ( 'previous' === this.getAttribute( 'direction' ) ) { + slider.previous(); + } else if ( 'next' === this.getAttribute( 'direction' ) ) { + slider.next(); + } + } + + +} diff --git a/src/new-slider/tp-slider-slide.ts b/src/new-slider/tp-slider-slide.ts new file mode 100644 index 0000000..4217a65 --- /dev/null +++ b/src/new-slider/tp-slider-slide.ts @@ -0,0 +1,20 @@ +/** + * Internal dependencies. + */ + +/** + * TP Slider. + */ +export class TPSliderSlideElement extends HTMLElement { + + + /** + * Constructor. + */ + constructor() { + + super(); + + + } +} diff --git a/src/new-slider/tp-slider-slides.ts b/src/new-slider/tp-slider-slides.ts new file mode 100644 index 0000000..8a09a7f --- /dev/null +++ b/src/new-slider/tp-slider-slides.ts @@ -0,0 +1,20 @@ +/** + * Internal dependencies. + */ + +/** + * TP Slider. + */ +export class TPSliderSlidesElement extends HTMLElement { + + + /** + * Constructor. + */ + constructor() { + + super(); + + + } +} diff --git a/src/new-slider/tp-slider-track.ts b/src/new-slider/tp-slider-track.ts new file mode 100644 index 0000000..d08890b --- /dev/null +++ b/src/new-slider/tp-slider-track.ts @@ -0,0 +1,20 @@ +/** + * Internal dependencies. + */ + +/** + * TP Slider. + */ +export class TPSliderTrackElement extends HTMLElement { + + + /** + * Constructor. + */ + constructor() { + + super(); + + + } +} diff --git a/src/new-slider/tp-slider.ts b/src/new-slider/tp-slider.ts index 44514b4..d75e4a2 100644 --- a/src/new-slider/tp-slider.ts +++ b/src/new-slider/tp-slider.ts @@ -1,457 +1,375 @@ /** - * components/tp-slider.ts - Main slider component that orchestrates the functionality + * Internal dependencies. */ -export class TPSlider extends HTMLElement { - // Properties - private currentIndex: number = 0; - private autoPlayInterval: number | null = null; - private touchStartX: number = 0; - private touchEndX: number = 0; - private responsiveSettings: any[] = []; - private behaviour: string = 'slide'; // 'slide' or 'fade' - - // Elements (will be populated in connectedCallback) - private track: HTMLElement | null = null; - private slidesContainer: HTMLElement | null = null; - private slides: HTMLElement[] = []; - private navItems: NodeListOf | null = null; - private countElement: HTMLElement | null = null; - private prevArrow: HTMLElement | null = null; - private nextArrow: HTMLElement | null = null; - - // Getters for attributes - get infiniteScroll(): boolean { - return this.hasAttribute( 'infinite' ) && this.getAttribute( 'infinite' ) !== 'no'; - } - - // TODO: Add comment. - get autoSlideInterval(): number { - return this.hasAttribute( 'auto-slide-interval' ) - ? parseInt( this.getAttribute( 'auto-slide-interval' ) || '0', 10 ) - : 0; - } - // TODO: Add comment. - get perView(): number { - return this.hasAttribute( 'per-view' ) - ? parseInt( this.getAttribute( 'per-view' ) || '1', 10 ) - : 1; - } +import { TPSliderArrowElement } from "./tp-slider-arrow"; +import { TPSliderSlideElement } from "./tp-slider-slide"; +import { TPSliderSlidesElement } from "./tp-slider-slides"; - // TODO: Add comment. - get step(): number { - return this.hasAttribute( 'step' ) - ? parseInt( this.getAttribute( 'step' ) || '1', 10 ) - : 1; - } - - // TODO: Add comment. - get flexibleHeight(): boolean { - return this.hasAttribute( 'flexible-height' ) && this.getAttribute( 'flexible-height' ) !== 'no'; - } - - // TODO: Add comment. - get swipeEnabled(): boolean { - return this.hasAttribute( 'swipe' ) && this.getAttribute( 'swipe' ) !== 'no'; - } - - // TODO: Add comment. - get swipeThreshold(): number { - return this.hasAttribute( 'swipe-threshold' ) - ? parseInt( this.getAttribute( 'swipe-threshold' ) || '50', 10 ) - : 50; - } - - // TODO: Add comment. +/** + * TP Slider. + */ +export class TPSliderElement extends HTMLElement { + /** + * Constructor. + */ constructor() { + // Initialize parent. super(); - } - - // TODO: Add comment. - connectedCallback() { - // Parse responsive settings first - this.parseResponsiveSettings(); - - // Get all the necessary elements - this.track = this.querySelector( 'tp-slider-track' ); - this.slidesContainer = this.querySelector( 'tp-slider-slides' ); - this.slides = Array.from( this.querySelectorAll( 'tp-slider-slide' ) ); - this.navItems = this.querySelectorAll( 'tp-slider-nav-item' ); - this.countElement = this.querySelector( 'tp-slider-count' ); - this.prevArrow = this.querySelector( 'tp-slider-arrow[direction="previous"]' ); - this.nextArrow = this.querySelector( 'tp-slider-arrow[direction="next"]' ); - - // Apply responsive settings - this.applyResponsiveSettings(); - - // Set up event listeners - this.setupEventListeners(); - // Initialize slider state - this.updateSliderState(); - - // Start auto-sliding if enabled - this.setupAutoPlay(); - } - - // TODO: Add comment. - disconnectedCallback() { - // Clean up event listeners - this.removeEventListeners(); - - // Stop auto-play - if ( this.autoPlayInterval ) { - clearInterval( this.autoPlayInterval ); - this.autoPlayInterval = null; + // Set current slide. + if (!this.getAttribute("current-slide")) { + this.setAttribute("current-slide", "1"); } - } - // TODO: Add comment. - static get observedAttributes() { - return [ - 'per-view', - 'step', - 'infinite', - 'auto-slide-interval', - 'behaviour', - 'flexible-height', - 'swipe', - 'swipe-threshold', - 'responsive', - ]; + // Initialize slider. + this.slide(); + this.setAttribute( 'initialized', 'yes' ); } - // TODO: Add comment. - attributeChangedCallback( name: string, oldValue: string, newValue: string ) { - if ( oldValue === newValue ) { - return; - } - - // TODO: Add comment. - switch ( name ) { - case 'responsive': - this.parseResponsiveSettings(); - this.applyResponsiveSettings(); - break; - case 'behaviour': - this.behaviour = newValue || 'slide'; - this.updateSliderState(); - break; - case 'per-view': - case 'step': - case 'infinite': - case 'flexible-height': - this.updateSliderState(); - break; - case 'auto-slide-interval': - this.setupAutoPlay(); - break; - case 'swipe': - this.updateSwipeHandlers(); - break; - } - } - - // Parse responsive settings from attribute - private parseResponsiveSettings(): void { - if ( this.hasAttribute( 'responsive' ) ) { - try { - this.responsiveSettings = JSON.parse( this.getAttribute( 'responsive' ) || '[]' ); - } catch ( e ) { - console.error( 'Invalid responsive settings JSON:', e ); - this.responsiveSettings = []; - } - } + /** + * Connected callback. + */ + connectedCallback() { + /** + * Update on initial render. + * + * This is so that the disabled values of the navigation arrows + * can be set because attributeChangedCallback does not get fired when + * no attributes are passed to the slider. + */ + this.update(); } - // Apply responsive settings based on current viewport - private applyResponsiveSettings(): void { - // Loop through responsive settings in reverse order (mobile-first approach) - for ( const setting of this.responsiveSettings ) { - if ( window.matchMedia( setting.media ).matches ) { - // Apply settings to attributes - Object.entries( setting ).forEach( ( [ key, value ] ) => { - if ( key !== 'media' ) { - this.setAttribute( key, String( value ) ); - } - } ); - break; // Apply only the first matching media query - } - } + /** + * Get observed attributes. + * + * @return {Array} List of observed attributes. + */ + static get observedAttributes(): string[] { + // Observed attributes. + return [ 'current-slide', 'infinite' ]; } - // Set up event listeners - private setupEventListeners(): void { - // Arrow navigation - if ( this.prevArrow ) { - this.prevArrow.addEventListener( 'click', () => this.prev() ); + /** + * Attribute changed callback. + * + * @param {string} name Attribute name. + * @param {string} oldValue Old value. + * @param {string} newValue New value. + */ + attributeChangedCallback( name: string = '', oldValue: string = '', newValue: string = '' ): void { + // Keep an eye on current slide. + if ( 'current-slide' === name && oldValue !== newValue ) { + this.slide(); + this.dispatchEvent( new CustomEvent( 'slide-complete', { bubbles: true } ) ); } - // TODO: Add comment. - if ( this.nextArrow ) { - this.nextArrow.addEventListener( 'click', () => this.next() ); - } - - // Nav item clicks - if ( this.navItems ) { - this.navItems.forEach( ( item, index ) => { - item.addEventListener( 'click', () => this.goToSlide( index ) ); - } ); - } - - // Swipe handlers - this.updateSwipeHandlers(); - - // Window resize handler - window.addEventListener( 'resize', this.handleResize.bind( this ) ); + // Update the component after the attribute change. + this.update(); + console.log(); + } - // Remove event listeners - private removeEventListeners(): void { - window.removeEventListener( 'resize', this.handleResize.bind( this ) ); + /** + * Get current slide index. + * + * @return {number} Current slide index. + */ + get currentSlideIndex(): number { + // To get the current slide index. + return parseInt(this.getAttribute("current-slide") ?? "1"); + } - // Remove swipe handlers - if ( this.slidesContainer ) { - this.slidesContainer.removeEventListener( 'touchstart', this.handleTouchStart.bind( this ) ); - this.slidesContainer.removeEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); - } + /** + * Set current slide index. + * + * @param {number} index Slide index. + */ + set currentSlideIndex(index: number) { + // Set the current slide index. + this.setCurrentSlide(index); } - // Handle window resize - private handleResize(): void { - this.applyResponsiveSettings(); - this.updateSliderState(); + /** + * Get Slide Elements. + */ + getSlideElements() { + // Get slides. + const slidesElement: TPSliderSlidesElement | null = + this.querySelector("tp-slider-slides"); + const slides: NodeListOf | null | undefined = + slidesElement?.querySelectorAll(":scope > tp-slider-slide"); + + // Return array of slides. + return slides; } - // Update swipe handlers - private updateSwipeHandlers(): void { - if ( ! this.slidesContainer ) { + /** + * Slide to the current slide. + * + * @protected + */ + protected slide(): void { + // Check if slider is disabled. + if ("yes" === this.getAttribute("disabled")) { + // Yes, it is. So stop. return; } - // Remove existing listeners to avoid duplicates - this.slidesContainer.removeEventListener( 'touchstart', this.handleTouchStart.bind( this ) ); - this.slidesContainer.removeEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); + // Get slides. + const slidesContainer: TPSliderSlidesElement | null = this.querySelector("tp-slider-slides"); + const slides: NodeListOf | null | undefined = this.getSlideElements(); - // Add new listeners if swipe is enabled - if ( this.swipeEnabled ) { - this.slidesContainer.addEventListener( 'touchstart', this.handleTouchStart.bind( this ), { passive: true } ); - this.slidesContainer.addEventListener( 'touchend', this.handleTouchEnd.bind( this ) ); + // Check if we have slide container and slides. + if (!slidesContainer || !slides) { + // No, we don't. Either one of them or both are missing. So stop. + return; } - } + console.log(slides); + - // Handle touch start - private handleTouchStart( e: TouchEvent ): void { - this.touchStartX = e.changedTouches[ 0 ].screenX; - } + // First, update the height. - // Handle touch end - private handleTouchEnd( e: TouchEvent ): void { - this.touchEndX = e.changedTouches[ 0 ].screenX; - this.handleSwipe(); - } + // Yield to main thread to fix a bug in Safari 16. + // setTimeout( () => this.updateHeight(), 0 ); - // Handle swipe gesture - private handleSwipe(): void { - const swipeDistance = this.touchEndX - this.touchStartX; + // Now lets slide! + const behaviour: string = this.getAttribute("behaviour") || ""; - // TODO: Add comment. - if ( Math.abs( swipeDistance ) < this.swipeThreshold ) { - return; - } - - // TODO: Add comment. - if ( swipeDistance < 0 ) { - // Swipe left - go next - this.next(); - } else { - // Swipe right - go previous - this.prev(); + // 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.scroll( { + left: slides[this.currentSlideIndex - 1].offsetLeft - slides[0].offsetLeft, + behavior: 'smooth', + }); } } - // Set up auto-play - private setupAutoPlay(): void { - // Clear existing interval - if ( this.autoPlayInterval ) { - clearInterval( this.autoPlayInterval ); - this.autoPlayInterval = null; - } - - // Set up new interval if needed - if ( this.autoSlideInterval > 0 ) { - this.autoPlayInterval = window.setInterval( () => this.next(), this.autoSlideInterval ); - - // Pause on hover - this.addEventListener( 'mouseenter', () => { - if ( this.autoPlayInterval ) { - clearInterval( this.autoPlayInterval ); - this.autoPlayInterval = null; + /** + * Get the arrow element by selector. + * + * In case of nested sliders, it difficult to find the correct arrow + * because arrows can be placed anywhere. + * This function checks if the parent tp-slider belongs to this component, + * then return that arrow element, using 'this'. + * + * @param {string} selector Selector. + */ + getArrow( selector: string ) { + // Get all arrows. + const arrows: NodeListOf | null = this.querySelectorAll( selector ); + const parentSliderElement: TPSliderElement = this; + let theArrow: TPSliderArrowElement | null = this.querySelector( selector ); + + // Loop through all the arrows including the one's inside nested slider. + arrows.forEach( ( arrow ) => { + /** + * If the closest tp-slider is the same as the parentSliderElement, that means we have found + * the correct arrow. + */ + if ( parentSliderElement === arrow.closest( 'tp-slider' ) ) { + theArrow = arrow; } } ); - - // TODO: Add comment. - this.addEventListener( 'mouseleave', () => { - if ( ! this.autoPlayInterval && this.autoSlideInterval > 0 ) { - this.autoPlayInterval = window.setInterval( () => this.next(), this.autoSlideInterval ); - } - } ); - } - } - - // Update slider state based on current settings - private updateSliderState(): void { - if ( ! this.slidesContainer || this.slides.length === 0 ) { - return; + + // Return arrow. + return theArrow; } - // Update behaviour-specific styles - if ( this.behaviour === 'fade' ) { - // Fade behaviour - this.slidesContainer.classList.add( 'tp-fade-behaviour' ); - this.slidesContainer.classList.remove( 'tp-slide-behaviour' ); - - // Update slide visibility - this.slides.forEach( ( slide, index ) => { - slide.classList.toggle( 'active', index === this.currentIndex ); - } ); - } else { - // Slide behaviour - this.slidesContainer.classList.add( 'tp-slide-behaviour' ); - this.slidesContainer.classList.remove( 'tp-fade-behaviour' ); - - // Set slide width based on perView - this.slides.forEach( ( slide ) => { - slide.style.flex = `0 0 calc(100% / ${ this.perView })`; - } ); + /** + * Get current slide index. + * + * @return {number} Current slide index. + */ + getCurrentSlide(): number { + // Get current slide index. + return this.currentSlideIndex; + } - // Position the slides - this.scrollToSlide( this.currentIndex ); + /** + * Update stuff when any attribute has changed. + * Example: Update sub-components. + */ + update(): void { + // Get sub-components. + // const sliderNav: TPSliderNavElement | null = this.querySelector( 'tp-slider-nav' ); + // const sliderCounts: NodeListOf | null = this.querySelectorAll( 'tp-slider-count' ); + const leftArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="previous"]' ); + const rightArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="next"]' ); + + // Set active slide. + const slides: NodeListOf | null | undefined = this.getSlideElements(); + + // Check if slides are available. + if ( slides ) { + slides.forEach( ( slide: TPSliderSlideElement, index: number ): void => { + // Update active attribute. + if ( this.currentSlideIndex - 1 === index ) { + slide.setAttribute( 'active', 'yes' ); + } else { + slide.removeAttribute( 'active' ); + } + } ); + } + + // First, set the template for the slider nav. + // sliderNav?.updateNavItems(); + + // Once the template has been set, query the slider nav items. + // const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); + + // Set current slider nav item. + // if ( sliderNavItems ) { + // sliderNavItems.forEach( ( navItem: TPSliderNavItemElement, index: number ): void => { + // // Update current attribute after considering step. + // if ( Math.ceil( this.currentSlideIndex / this.step ) - 1 === index ) { + // navItem.setAttribute( 'current', 'yes' ); + // } else { + // navItem.removeAttribute( 'current' ); + // } + // } ); + // } + + // Update slider count. + // if ( sliderCounts ) { + // // Set total attribute. + // this.setAttribute( 'total', this.getTotalSlides().toString() ); + + // // Update slider counts. + // sliderCounts.forEach( ( slideCount: TPSliderCountElement ) => { + // // Check if the slideCount.update is a function. + // if ( 'function' === typeof slideCount.update ) { + // // Update slide count. + // slideCount.update(); + // } + // } ); + // } + + // Enable / disable arrows. + if ( 'yes' !== this.getAttribute( 'infinite' ) ) { + // For the last slide. + if ( this.getCurrentSlide() === this.getTotalSlides() + 1 ) { + rightArrow?.setAttribute( 'disabled', 'yes' ); + } else { + rightArrow?.removeAttribute( 'disabled' ); + } + + // For the first slide. + if ( 1 === this.getCurrentSlide() ) { + leftArrow?.setAttribute( 'disabled', 'yes' ); + } else { + leftArrow?.removeAttribute( 'disabled' ); + } + } + // else { + // rightArrow?.removeAttribute( 'disabled' ); + // leftArrow?.removeAttribute( 'disabled' ); + // } } - // Update flexible height - if ( this.flexibleHeight ) { - this.track?.classList.add( 'flexible-height' ); - } else { - this.track?.classList.remove( 'flexible-height' ); + /** + * Get total number of slides. + * + * @return {number} Total slides. + */ + getTotalSlides(): number { + // To get the total number of slides. + const slides: NodeListOf | null | undefined = + this.getSlideElements(); + + // Check if slides are available. + if (slides) { + // Tell the total number of slides. + return slides.length; } - // Update navigation state - this.updateNavigation(); - - // Update count - this.updateCount(); + // Else return 0. + return 0; } - // Go to next slide - public next(): void { - const newIndex = this.currentIndex + this.step; - - // TODO: Add comment. - if ( newIndex >= this.slides.length ) { - if ( this.infiniteScroll ) { - // Loop back to the beginning - this.goToSlide( 0 ); - } else { - // Stay at the last slide - this.goToSlide( this.slides.length - 1 ); - } - } else { - this.goToSlide( newIndex ); + /** + * Set the current slide index. + * + * @param {number} index Slide index. + */ + setCurrentSlide(index: number): void { + // Check if slide index is valid. + if (index > this.getTotalSlides() || index <= 0) { + // Stop! It's not valid. + return; } - } - // Go to previous slide - public prev(): void { - const newIndex = this.currentIndex - this.step; - - // TODO: Add comment. - if ( newIndex < 0 ) { - if ( this.infiniteScroll ) { - // Loop to the end - this.goToSlide( this.slides.length - 1 ); - } else { - // Stay at the first slide - this.goToSlide( 0 ); - } - } else { - this.goToSlide( newIndex ); - } + // dispatch slide-set event. + this.dispatchEvent( + new CustomEvent("slide-set", { + bubbles: true, + detail: { + slideIndex: index, + }, + }), + ); + + // Set current slide index. + this.setAttribute("current-slide", index.toString()); } - // Go to specific slide - public goToSlide( index: number ): void { - // Constrain index to valid range - const safeIndex = Math.max( 0, Math.min( index, this.slides.length - 1 ) ); - - // Update current index - this.currentIndex = safeIndex; + /** + * Navigate to the next slide. + */ + next(): void { + // Initialize total slides variable. + const totalSlides: number = this.getTotalSlides(); + + // Check if we are at the last slide considering per view attribute. + if ( this.currentSlideIndex >= totalSlides + 1 ) { + // Check if we are in infinite mode. + if ( 'yes' === this.getAttribute( 'infinite' ) ) { + // Yes, we are, and go back to first slide. + this.setCurrentSlide( 1 ); + } - // Update slides based on behaviour - if ( this.behaviour === 'fade' ) { - this.slides.forEach( ( slide, i ) => { - slide.classList.toggle( 'active', i === this.currentIndex ); - } ); - } else { - this.scrollToSlide( this.currentIndex ); + // Terminate. + return; } - // Update navigation - this.updateNavigation(); + // Get next slide index by adding minimum of step or remaining number of slides. + const nextSlideIndex: number = this.currentSlideIndex + 1; - // Update count - this.updateCount(); - - // Dispatch event - this.dispatchEvent( new CustomEvent( 'slide-change', { - detail: { index: this.currentIndex }, - } ) ); - } - - // Scroll to specific slide (for slide behaviour) - private scrollToSlide( index: number ): void { - if ( ! this.slidesContainer || ! this.slides[ index ] ) { + // Check if the next slide step is not taking it beyond the last slide. + if ( nextSlideIndex > ( totalSlides + 1 ) ) { + // Yes, it is. return; } - // TODO: Add comment. - const slideWidth = this.slides[ 0 ].offsetWidth; - const offset = slideWidth * index; - - // TODO: Add comment. - this.slidesContainer.style.transform = `translateX(-${ offset }px)`; + // Everything is good, go to next slide. + this.setCurrentSlide( nextSlideIndex ); } - // Update navigation state - private updateNavigation(): void { - if ( this.navItems ) { - this.navItems.forEach( ( item, index ) => { - item.classList.toggle( 'active', index === this.currentIndex ); - } ); - } - } + /** + * Navigate to the previous slide. + */ + previous(): void { + // Check if we are at the first slide. + if ( this.currentSlideIndex <= 1 ) { + // Check if we are in infinite mode. + if ( 'yes' === this.getAttribute( 'infinite' ) ) { + this.setCurrentSlide( this.getTotalSlides() + 1 ); + } - // Update count element - private updateCount(): void { - if ( ! this.countElement ) { + // Terminate. return; } - // TODO: Add comment. - const current = this.currentIndex + 1; - const total = this.slides.length; - - // Update attributes - this.countElement.setAttribute( 'current', current.toString() ); - this.countElement.setAttribute( 'total', total.toString() ); + // Get previous slide index. + const previousSlideNumber: number = this.currentSlideIndex; - // Update content based on format - const format = this.countElement.getAttribute( 'format' ) || '$current / $total'; - const formattedCount = format - .replace( '$current', current.toString() ) - .replace( '$total', total.toString() ); - - // TODO: Add comment. - this.countElement.textContent = formattedCount; + // Check if the previous slide step is not taking it beyond the first slide. + if ( previousSlideNumber > 1 ) { + this.setCurrentSlide( previousSlideNumber - 1 ); + } else { + this.setCurrentSlide( 1 ); + } } } From 76d59ede60ddfd775fd78ab551231e14aed873c1 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Fri, 2 May 2025 10:53:45 +0530 Subject: [PATCH 27/31] WP-144 Handle infinite attribute --- src/new-slider/index.html | 2 +- src/new-slider/tp-slider.ts | 251 +++++++++++++++++++++--------------- 2 files changed, 146 insertions(+), 107 deletions(-) diff --git a/src/new-slider/index.html b/src/new-slider/index.html index 0932a72..0de6061 100644 --- a/src/new-slider/index.html +++ b/src/new-slider/index.html @@ -28,7 +28,7 @@
- + diff --git a/src/new-slider/tp-slider.ts b/src/new-slider/tp-slider.ts index d75e4a2..1764cbc 100644 --- a/src/new-slider/tp-slider.ts +++ b/src/new-slider/tp-slider.ts @@ -10,6 +10,9 @@ import { TPSliderSlidesElement } from "./tp-slider-slides"; * TP Slider. */ export class TPSliderElement extends HTMLElement { + // protected _observer: IntersectionObserver; + protected slides: NodeListOf | null; + protected slideTrack: TPSliderSlidesElement | null; /** * Constructor. */ @@ -17,6 +20,10 @@ export class TPSliderElement extends HTMLElement { // Initialize parent. super(); + this.slides = this.querySelectorAll( 'tp-slider-slide' ); + this.slideTrack = this.querySelector( 'tp-slider-slides' ); + // this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 0.999 } ); + // Set current slide. if (!this.getAttribute("current-slide")) { this.setAttribute("current-slide", "1"); @@ -25,6 +32,9 @@ export class TPSliderElement extends HTMLElement { // Initialize slider. this.slide(); this.setAttribute( 'initialized', 'yes' ); + + // Observe which slide is in view. + // this.slides.forEach(( slide ) => this._observer.observe( slide ) ); } /** @@ -41,6 +51,20 @@ export class TPSliderElement extends HTMLElement { this.update(); } + /** + * Change current-slide attribute on scroll. + */ + // attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { + // entries?.forEach( ( entry ) => { + // if (entry.isIntersecting && entry.target instanceof TPSliderSlideElement && this.slides) { + // const index = Array.from(this.slides).indexOf(entry.target); + // this.currentSlideIndex = index + 1; + // } + // } ); + + // } + + /** * Get observed attributes. * @@ -48,7 +72,7 @@ export class TPSliderElement extends HTMLElement { */ static get observedAttributes(): string[] { // Observed attributes. - return [ 'current-slide', 'infinite' ]; + return [ 'current-slide', 'infinite', 'per-view' ]; } /** @@ -67,8 +91,6 @@ export class TPSliderElement extends HTMLElement { // Update the component after the attribute change. this.update(); - console.log(); - } /** @@ -91,15 +113,33 @@ export class TPSliderElement extends HTMLElement { this.setCurrentSlide(index); } + /** + * Get per view. + * + * @return {number} Current step. + */ + get perView(): number { + // To get number of slides per view. + return parseInt( this.getAttribute( 'per-view' ) ?? '1' ); + } + + /** + * Set per view. + * + * @param {number} perView Per view. + */ + set perView( perView: number ) { + // Set the number of slides per view. + this.setAttribute( 'per-view', perView.toString() ); + } + /** * Get Slide Elements. */ getSlideElements() { // Get slides. - const slidesElement: TPSliderSlidesElement | null = - this.querySelector("tp-slider-slides"); - const slides: NodeListOf | null | undefined = - slidesElement?.querySelectorAll(":scope > tp-slider-slide"); + const slidesElement: TPSliderSlidesElement | null = this.querySelector("tp-slider-slides"); + const slides: NodeListOf | null | undefined = slidesElement?.querySelectorAll(":scope > tp-slider-slide"); // Return array of slides. return slides; @@ -126,7 +166,6 @@ export class TPSliderElement extends HTMLElement { // No, we don't. Either one of them or both are missing. So stop. return; } - console.log(slides); // First, update the height. @@ -157,26 +196,26 @@ export class TPSliderElement extends HTMLElement { * * @param {string} selector Selector. */ - getArrow( selector: string ) { - // Get all arrows. - const arrows: NodeListOf | null = this.querySelectorAll( selector ); - const parentSliderElement: TPSliderElement = this; - let theArrow: TPSliderArrowElement | null = this.querySelector( selector ); - - // Loop through all the arrows including the one's inside nested slider. - arrows.forEach( ( arrow ) => { - /** - * If the closest tp-slider is the same as the parentSliderElement, that means we have found - * the correct arrow. - */ - if ( parentSliderElement === arrow.closest( 'tp-slider' ) ) { - theArrow = arrow; - } - } ); - - // Return arrow. - return theArrow; - } + getArrow( selector: string ) { + // Get all arrows. + const arrows: NodeListOf | null = this.querySelectorAll( selector ); + const parentSliderElement: TPSliderElement = this; + let theArrow: TPSliderArrowElement | null = this.querySelector( selector ); + + // Loop through all the arrows including the one's inside nested slider. + arrows.forEach( ( arrow ) => { + /** + * If the closest tp-slider is the same as the parentSliderElement, that means we have found + * the correct arrow. + */ + if ( parentSliderElement === arrow.closest( 'tp-slider' ) ) { + theArrow = arrow; + } + } ); + + // Return arrow. + return theArrow; + } /** * Get current slide index. @@ -192,83 +231,83 @@ export class TPSliderElement extends HTMLElement { * Update stuff when any attribute has changed. * Example: Update sub-components. */ - update(): void { - // Get sub-components. - // const sliderNav: TPSliderNavElement | null = this.querySelector( 'tp-slider-nav' ); - // const sliderCounts: NodeListOf | null = this.querySelectorAll( 'tp-slider-count' ); - const leftArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="previous"]' ); - const rightArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="next"]' ); - - // Set active slide. - const slides: NodeListOf | null | undefined = this.getSlideElements(); - - // Check if slides are available. - if ( slides ) { - slides.forEach( ( slide: TPSliderSlideElement, index: number ): void => { - // Update active attribute. - if ( this.currentSlideIndex - 1 === index ) { - slide.setAttribute( 'active', 'yes' ); - } else { - slide.removeAttribute( 'active' ); - } - } ); - } - - // First, set the template for the slider nav. - // sliderNav?.updateNavItems(); - - // Once the template has been set, query the slider nav items. - // const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); - - // Set current slider nav item. - // if ( sliderNavItems ) { - // sliderNavItems.forEach( ( navItem: TPSliderNavItemElement, index: number ): void => { - // // Update current attribute after considering step. - // if ( Math.ceil( this.currentSlideIndex / this.step ) - 1 === index ) { - // navItem.setAttribute( 'current', 'yes' ); - // } else { - // navItem.removeAttribute( 'current' ); - // } - // } ); - // } - - // Update slider count. - // if ( sliderCounts ) { - // // Set total attribute. - // this.setAttribute( 'total', this.getTotalSlides().toString() ); - - // // Update slider counts. - // sliderCounts.forEach( ( slideCount: TPSliderCountElement ) => { - // // Check if the slideCount.update is a function. - // if ( 'function' === typeof slideCount.update ) { - // // Update slide count. - // slideCount.update(); - // } - // } ); - // } - - // Enable / disable arrows. - if ( 'yes' !== this.getAttribute( 'infinite' ) ) { - // For the last slide. - if ( this.getCurrentSlide() === this.getTotalSlides() + 1 ) { - rightArrow?.setAttribute( 'disabled', 'yes' ); - } else { - rightArrow?.removeAttribute( 'disabled' ); - } - - // For the first slide. - if ( 1 === this.getCurrentSlide() ) { - leftArrow?.setAttribute( 'disabled', 'yes' ); + update(): void { + // Get sub-components. + // const sliderNav: TPSliderNavElement | null = this.querySelector( 'tp-slider-nav' ); + // const sliderCounts: NodeListOf | null = this.querySelectorAll( 'tp-slider-count' ); + const leftArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="previous"]' ); + const rightArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="next"]' ); + + // Set active slide. + const slides: NodeListOf | null | undefined = this.getSlideElements(); + + // Check if slides are available. + if ( slides ) { + slides.forEach( ( slide: TPSliderSlideElement, index: number ): void => { + // Update active attribute. + if ( this.currentSlideIndex - 1 === index ) { + slide.setAttribute( 'active', 'yes' ); } else { - leftArrow?.removeAttribute( 'disabled' ); + slide.removeAttribute( 'active' ); } - } - // else { - // rightArrow?.removeAttribute( 'disabled' ); - // leftArrow?.removeAttribute( 'disabled' ); - // } + } ); } + // First, set the template for the slider nav. + // sliderNav?.updateNavItems(); + + // Once the template has been set, query the slider nav items. + // const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); + + // Set current slider nav item. + // if ( sliderNavItems ) { + // sliderNavItems.forEach( ( navItem: TPSliderNavItemElement, index: number ): void => { + // // Update current attribute after considering step. + // if ( Math.ceil( this.currentSlideIndex / this.step ) - 1 === index ) { + // navItem.setAttribute( 'current', 'yes' ); + // } else { + // navItem.removeAttribute( 'current' ); + // } + // } ); + // } + + // Update slider count. + // if ( sliderCounts ) { + // // Set total attribute. + // this.setAttribute( 'total', this.getTotalSlides().toString() ); + + // // Update slider counts. + // sliderCounts.forEach( ( slideCount: TPSliderCountElement ) => { + // // Check if the slideCount.update is a function. + // if ( 'function' === typeof slideCount.update ) { + // // Update slide count. + // slideCount.update(); + // } + // } ); + // } + + // Enable / disable arrows. + if ( 'yes' !== this.getAttribute( 'infinite' ) ) { + // For the last slide. + if ( this.getCurrentSlide() === this.getTotalSlides() + 1 ) { + rightArrow?.setAttribute( 'disabled', 'yes' ); + } else { + rightArrow?.removeAttribute( 'disabled' ); + } + + // For the first slide. + if ( 1 === this.getCurrentSlide() ) { + leftArrow?.setAttribute( 'disabled', 'yes' ); + } else { + leftArrow?.removeAttribute( 'disabled' ); + } + } + // else { + // rightArrow?.removeAttribute( 'disabled' ); + // leftArrow?.removeAttribute( 'disabled' ); + // } + } + /** * Get total number of slides. * @@ -276,8 +315,7 @@ export class TPSliderElement extends HTMLElement { */ getTotalSlides(): number { // To get the total number of slides. - const slides: NodeListOf | null | undefined = - this.getSlideElements(); + const slides: NodeListOf | null | undefined = this.getSlideElements(); // Check if slides are available. if (slides) { @@ -321,9 +359,10 @@ export class TPSliderElement extends HTMLElement { next(): void { // Initialize total slides variable. const totalSlides: number = this.getTotalSlides(); - + + // Check if we are at the last slide considering per view attribute. - if ( this.currentSlideIndex >= totalSlides + 1 ) { + if ( this.currentSlideIndex >= totalSlides ) { // Check if we are in infinite mode. if ( 'yes' === this.getAttribute( 'infinite' ) ) { // Yes, we are, and go back to first slide. @@ -355,7 +394,7 @@ export class TPSliderElement extends HTMLElement { if ( this.currentSlideIndex <= 1 ) { // Check if we are in infinite mode. if ( 'yes' === this.getAttribute( 'infinite' ) ) { - this.setCurrentSlide( this.getTotalSlides() + 1 ); + this.setCurrentSlide( this.getTotalSlides() ); } // Terminate. From f0b6b9e84cb62fcfc0150fe95eecf06093b9c3d4 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Fri, 2 May 2025 13:00:27 +0530 Subject: [PATCH 28/31] WP-144 Add per-view and step support --- src/new-slider/index.html | 23 ++++++++++++++++++++++- src/new-slider/tp-slider.ts | 32 ++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/new-slider/index.html b/src/new-slider/index.html index 0de6061..08b12d5 100644 --- a/src/new-slider/index.html +++ b/src/new-slider/index.html @@ -14,6 +14,11 @@ background-color: #eee } + tp-slider-slide img { + width: 100%; + height: auto; + } + img { display: block; } @@ -27,7 +32,7 @@
- + @@ -39,6 +44,22 @@ + + + + + + + + + + + + + + + +
diff --git a/src/new-slider/tp-slider.ts b/src/new-slider/tp-slider.ts index 1764cbc..c478133 100644 --- a/src/new-slider/tp-slider.ts +++ b/src/new-slider/tp-slider.ts @@ -133,6 +133,26 @@ export class TPSliderElement extends HTMLElement { this.setAttribute( 'per-view', perView.toString() ); } + /** + * Get current step. + * + * @return {number} Current step. + */ + get step(): number { + // To get the current step. + return parseInt( this.getAttribute( 'step' ) ?? '1' ); + } + + /** + * Set current step. + * + * @param {number} step Step. + */ + set step( step: number ) { + // Set the current step. + this.setAttribute( 'step', step.toString() ); + } + /** * Get Slide Elements. */ @@ -362,7 +382,7 @@ export class TPSliderElement extends HTMLElement { // Check if we are at the last slide considering per view attribute. - if ( this.currentSlideIndex >= totalSlides ) { + if ( this.currentSlideIndex >= totalSlides - this.perView + 1 ) { // Check if we are in infinite mode. if ( 'yes' === this.getAttribute( 'infinite' ) ) { // Yes, we are, and go back to first slide. @@ -374,10 +394,10 @@ export class TPSliderElement extends HTMLElement { } // Get next slide index by adding minimum of step or remaining number of slides. - const nextSlideIndex: number = this.currentSlideIndex + 1; + const nextSlideIndex: number = this.currentSlideIndex + Math.min( this.step, totalSlides - this.currentSlideIndex - this.perView + 1 ); // Check if the next slide step is not taking it beyond the last slide. - if ( nextSlideIndex > ( totalSlides + 1 ) ) { + if ( nextSlideIndex > ( totalSlides - this.perView + 1 ) ) { // Yes, it is. return; } @@ -394,7 +414,7 @@ export class TPSliderElement extends HTMLElement { if ( this.currentSlideIndex <= 1 ) { // Check if we are in infinite mode. if ( 'yes' === this.getAttribute( 'infinite' ) ) { - this.setCurrentSlide( this.getTotalSlides() ); + this.setCurrentSlide( this.getTotalSlides() - this.perView + 1 ); } // Terminate. @@ -405,8 +425,8 @@ export class TPSliderElement extends HTMLElement { const previousSlideNumber: number = this.currentSlideIndex; // Check if the previous slide step is not taking it beyond the first slide. - if ( previousSlideNumber > 1 ) { - this.setCurrentSlide( previousSlideNumber - 1 ); + if ( previousSlideNumber > this.step ) { + this.setCurrentSlide( previousSlideNumber - this.step ); } else { this.setCurrentSlide( 1 ); } From 83d6e581fe26e04c332a8233264712ad1f0dd166 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Thu, 22 May 2025 18:33:13 +0530 Subject: [PATCH 29/31] WP-144 Add logic for manual scroll --- src/carousel/index.html | 64 +++++ src/carousel/index.ts | 22 ++ src/{new-slider => carousel}/style.scss | 5 +- src/carousel/tp-carousel-arrow.ts | 47 ++++ src/carousel/tp-carousel-slide.ts | 8 + src/carousel/tp-carousel-slides.ts | 8 + src/carousel/tp-carousel-track.ts | 8 + .../tp-slider.ts => carousel/tp-carousel.ts} | 237 ++++++++---------- src/new-slider/index.html | 67 ----- src/new-slider/index.ts | 22 -- src/new-slider/slider-components.ts | 0 src/new-slider/tp-slider-arrow.ts | 53 ---- src/new-slider/tp-slider-slide.ts | 20 -- src/new-slider/tp-slider-slides.ts | 20 -- src/new-slider/tp-slider-track.ts | 20 -- webpack.config.js | 2 +- 16 files changed, 269 insertions(+), 334 deletions(-) create mode 100644 src/carousel/index.html create mode 100644 src/carousel/index.ts rename src/{new-slider => carousel}/style.scss (69%) create mode 100644 src/carousel/tp-carousel-arrow.ts create mode 100644 src/carousel/tp-carousel-slide.ts create mode 100644 src/carousel/tp-carousel-slides.ts create mode 100644 src/carousel/tp-carousel-track.ts rename src/{new-slider/tp-slider.ts => carousel/tp-carousel.ts} (53%) delete mode 100644 src/new-slider/index.html delete mode 100644 src/new-slider/index.ts delete mode 100644 src/new-slider/slider-components.ts delete mode 100644 src/new-slider/tp-slider-arrow.ts delete mode 100644 src/new-slider/tp-slider-slide.ts delete mode 100644 src/new-slider/tp-slider-slides.ts delete mode 100644 src/new-slider/tp-slider-track.ts diff --git a/src/carousel/index.html b/src/carousel/index.html new file mode 100644 index 0000000..095bb1e --- /dev/null +++ b/src/carousel/index.html @@ -0,0 +1,64 @@ + + + + + + + Web Component: Slider + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/src/carousel/index.ts b/src/carousel/index.ts new file mode 100644 index 0000000..ea94600 --- /dev/null +++ b/src/carousel/index.ts @@ -0,0 +1,22 @@ +/** + * Styles. + */ +import './style.scss'; + +/** + * Components. + */ +import { TPCarouselElement } from './tp-carousel'; +import { TPCarouselTrackElement } from './tp-carousel-track'; +import { TPCarouselSlidesElement } from './tp-carousel-slides'; +import { TPCarouselSlideElement } from './tp-carousel-slide'; +import { TPCarouselArrowElement } from './tp-carousel-arrow'; + +/** + * Register Components. + */ +customElements.define( 'tp-carousel', TPCarouselElement ); +customElements.define( 'tp-carousel-track', TPCarouselTrackElement ); +customElements.define( 'tp-carousel-slides', TPCarouselSlidesElement ); +customElements.define( 'tp-carousel-slide', TPCarouselSlideElement ); +customElements.define( 'tp-carousel-arrow', TPCarouselArrowElement ); diff --git a/src/new-slider/style.scss b/src/carousel/style.scss similarity index 69% rename from src/new-slider/style.scss rename to src/carousel/style.scss index fbc3bda..0cb669b 100644 --- a/src/new-slider/style.scss +++ b/src/carousel/style.scss @@ -1,13 +1,14 @@ -tp-slider-slides { +tp-carousel-slides { overflow-y: visible; overflow-x: auto; scroll-snap-type: x mandatory; display: flex; + scrollbar-width: none; } -tp-slider-slide { +tp-carousel-slide { flex: 0 0 100%; scroll-snap-align: start; } diff --git a/src/carousel/tp-carousel-arrow.ts b/src/carousel/tp-carousel-arrow.ts new file mode 100644 index 0000000..8ac4912 --- /dev/null +++ b/src/carousel/tp-carousel-arrow.ts @@ -0,0 +1,47 @@ +/** + * Internal dependencies. + */ +import { TPCarouselElement } from './tp-carousel'; + +/** + * TP Carousel Arrow. + */ +export class TPCarouselArrowElement extends HTMLElement { + /** + * Constructor. + */ + constructor() { + // Call the parent constructor. + super(); + + // Get the button and add event listener. + this.querySelector( 'button' )?.addEventListener( 'click', this.handleClick.bind( this ) ); + } + + /** + * Handle when the button is clicked. + */ + handleClick(): void { + // If disabled, do nothing. + if ( 'yes' === this.getAttribute( 'disabled' ) ) { + // Early return. + return; + } + + // Get the carousel. + const carousel: TPCarouselElement | null = this.closest( 'tp-carousel' ); + + // If no carousel, early return. + if ( ! carousel ) { + // Early return. + return; + } + + // Initiate carousel according to the direction of the button clicked. + if ( 'previous' === this.getAttribute( 'direction' ) ) { + carousel.previous(); + } else if ( 'next' === this.getAttribute( 'direction' ) ) { + carousel.next(); + } + } +} diff --git a/src/carousel/tp-carousel-slide.ts b/src/carousel/tp-carousel-slide.ts new file mode 100644 index 0000000..0d96c38 --- /dev/null +++ b/src/carousel/tp-carousel-slide.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies. + */ + +/** + * TP Carousel. + */ +export class TPCarouselSlideElement extends HTMLElement {} diff --git a/src/carousel/tp-carousel-slides.ts b/src/carousel/tp-carousel-slides.ts new file mode 100644 index 0000000..86fee93 --- /dev/null +++ b/src/carousel/tp-carousel-slides.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies. + */ + +/** + * TP Carousel. + */ +export class TPCarouselSlidesElement extends HTMLElement {} diff --git a/src/carousel/tp-carousel-track.ts b/src/carousel/tp-carousel-track.ts new file mode 100644 index 0000000..7383b9f --- /dev/null +++ b/src/carousel/tp-carousel-track.ts @@ -0,0 +1,8 @@ +/** + * Internal dependencies. + */ + +/** + * TP Carousel. + */ +export class TPCarouselTrackElement extends HTMLElement {} diff --git a/src/new-slider/tp-slider.ts b/src/carousel/tp-carousel.ts similarity index 53% rename from src/new-slider/tp-slider.ts rename to src/carousel/tp-carousel.ts index c478133..ac2dcfa 100644 --- a/src/new-slider/tp-slider.ts +++ b/src/carousel/tp-carousel.ts @@ -1,69 +1,77 @@ /** * Internal dependencies. */ - -import { TPSliderArrowElement } from "./tp-slider-arrow"; -import { TPSliderSlideElement } from "./tp-slider-slide"; -import { TPSliderSlidesElement } from "./tp-slider-slides"; +import { TPCarouselArrowElement } from './tp-carousel-arrow'; +import { TPCarouselSlideElement } from './tp-carousel-slide'; +import { TPCarouselSlidesElement } from './tp-carousel-slides'; /** - * TP Slider. + * TP Carousel. */ -export class TPSliderElement extends HTMLElement { - // protected _observer: IntersectionObserver; - protected slides: NodeListOf | null; - protected slideTrack: TPSliderSlidesElement | null; +export class TPCarouselElement extends HTMLElement { + protected _observer: IntersectionObserver; + protected slides: NodeListOf | null; + protected slideTrack: TPCarouselSlidesElement | null; + protected isProgramaticScroll: boolean = false; + /** * Constructor. */ constructor() { // Initialize parent. super(); + this.slides = this.querySelectorAll( 'tp-carousel-slide' ); + this.slideTrack = this.querySelector( 'tp-carousel-slides' ); + this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 0.999 } ); - this.slides = this.querySelectorAll( 'tp-slider-slide' ); - this.slideTrack = this.querySelector( 'tp-slider-slides' ); - // this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 0.999 } ); - // Set current slide. - if (!this.getAttribute("current-slide")) { - this.setAttribute("current-slide", "1"); + if ( ! this.getAttribute( 'current-slide' ) ) { + this.setAttribute( 'current-slide', '1' ); } - // Initialize slider. + // Initialize carousel. this.slide(); this.setAttribute( 'initialized', 'yes' ); // Observe which slide is in view. - // this.slides.forEach(( slide ) => this._observer.observe( slide ) ); + this.slides.forEach( ( slide ) => this._observer.observe( slide ) ); } /** * Connected callback. */ connectedCallback() { - /** - * Update on initial render. - * - * This is so that the disabled values of the navigation arrows - * can be set because attributeChangedCallback does not get fired when - * no attributes are passed to the slider. - */ + // Update on initial render. this.update(); } /** * Change current-slide attribute on scroll. + * + * @param {IntersectionObserverEntry[]} entries slides which enter or leave on scroll. */ - // attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { - // entries?.forEach( ( entry ) => { - // if (entry.isIntersecting && entry.target instanceof TPSliderSlideElement && this.slides) { - // const index = Array.from(this.slides).indexOf(entry.target); - // this.currentSlideIndex = index + 1; - // } - // } ); - - // } - + attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { + // If the scroll is programatic. + if ( this.isProgramaticScroll ) { + // Do nothing. + return; + } + + // Change the current slide index when slide comes into view. + entries?.forEach( ( entry ) => { + // Check if the entry is intersecting with the slide track. + if ( entry.isIntersecting && entry.target instanceof TPCarouselSlideElement && this.slides ) { + const index = Array.from( this.slides ).indexOf( entry.target ); + + // Update current slide index based on if it is is right or left scroll. + if ( index + 1 - ( this.perView - 1 ) > this.currentSlideIndex ) { + this.currentSlideIndex = index + 1 - ( this.perView - 1 ); + } else if ( index + 1 - ( this.perView - 1 ) < this.currentSlideIndex ) { + this.currentSlideIndex = index + 1; + } + } + } ); + } /** * Get observed attributes. @@ -100,7 +108,7 @@ export class TPSliderElement extends HTMLElement { */ get currentSlideIndex(): number { // To get the current slide index. - return parseInt(this.getAttribute("current-slide") ?? "1"); + return parseInt( this.getAttribute( 'current-slide' ) ?? '1' ); } /** @@ -108,9 +116,9 @@ export class TPSliderElement extends HTMLElement { * * @param {number} index Slide index. */ - set currentSlideIndex(index: number) { + set currentSlideIndex( index: number ) { // Set the current slide index. - this.setCurrentSlide(index); + this.setCurrentSlide( index ); } /** @@ -158,8 +166,8 @@ export class TPSliderElement extends HTMLElement { */ getSlideElements() { // Get slides. - const slidesElement: TPSliderSlidesElement | null = this.querySelector("tp-slider-slides"); - const slides: NodeListOf | null | undefined = slidesElement?.querySelectorAll(":scope > tp-slider-slide"); + const slidesElement: TPCarouselSlidesElement | null = this.querySelector( 'tp-carousel-slides' ); + const slides: NodeListOf | null | undefined = slidesElement?.querySelectorAll( ':scope > tp-carousel-slide' ); // Return array of slides. return slides; @@ -171,64 +179,55 @@ export class TPSliderElement extends HTMLElement { * @protected */ protected slide(): void { - // Check if slider is disabled. - if ("yes" === this.getAttribute("disabled")) { + // Check if carousel is disabled. + if ( 'yes' === this.getAttribute( 'disabled' ) ) { // Yes, it is. So stop. return; } // Get slides. - const slidesContainer: TPSliderSlidesElement | null = this.querySelector("tp-slider-slides"); - const slides: NodeListOf | null | undefined = this.getSlideElements(); + const slidesContainer: TPCarouselSlidesElement | null = this.querySelector( 'tp-carousel-slides' ); + const slides: NodeListOf | null | undefined = this.getSlideElements(); // Check if we have slide container and slides. - if (!slidesContainer || !slides) { + if ( ! slidesContainer || ! slides ) { // No, we don't. Either one of them or both are missing. So stop. return; } - - - // First, update the height. - - // Yield to main thread to fix a bug in Safari 16. - // setTimeout( () => this.updateHeight(), 0 ); - - // Now lets slide! - const behaviour: string = this.getAttribute("behaviour") || ""; // 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]) { + if ( slides[ this.currentSlideIndex - 1 ] ) { // Yes, it is. So slide to the current slide. - slidesContainer.scroll( { - left: slides[this.currentSlideIndex - 1].offsetLeft - slides[0].offsetLeft, + slidesContainer.scroll( { + left: slides[ this.currentSlideIndex - 1 ].offsetLeft - slides[ 0 ].offsetLeft, behavior: 'smooth', - }); + } ); } } /** - * Get the arrow element by selector. - * - * In case of nested sliders, it difficult to find the correct arrow - * because arrows can be placed anywhere. - * This function checks if the parent tp-slider belongs to this component, - * then return that arrow element, using 'this'. - * - * @param {string} selector Selector. - */ + * Get the arrow element by selector. + * + * In case of nested carousels, it difficult to find the correct arrow + * because arrows can be placed anywhere. + * This function checks if the parent tp-carousel belongs to this component, + * then return that arrow element, using 'this'. + * + * @param {string} selector Selector. + */ getArrow( selector: string ) { // Get all arrows. - const arrows: NodeListOf | null = this.querySelectorAll( selector ); - const parentSliderElement: TPSliderElement = this; - let theArrow: TPSliderArrowElement | null = this.querySelector( selector ); + const arrows: NodeListOf | null = this.querySelectorAll( selector ); + const parentCarouselElement: TPCarouselElement = this; + let theArrow: TPCarouselArrowElement | null = this.querySelector( selector ); - // Loop through all the arrows including the one's inside nested slider. + // Loop through all the arrows including the one's inside nested carousel. arrows.forEach( ( arrow ) => { /** - * If the closest tp-slider is the same as the parentSliderElement, that means we have found + * If the closest tp-carousel is the same as the parentCarouselElement, that means we have found * the correct arrow. */ - if ( parentSliderElement === arrow.closest( 'tp-slider' ) ) { + if ( parentCarouselElement === arrow.closest( 'tp-carousel' ) ) { theArrow = arrow; } } ); @@ -237,7 +236,7 @@ export class TPSliderElement extends HTMLElement { return theArrow; } - /** + /** * Get current slide index. * * @return {number} Current slide index. @@ -248,22 +247,20 @@ export class TPSliderElement extends HTMLElement { } /** - * Update stuff when any attribute has changed. - * Example: Update sub-components. - */ + * Update stuff when any attribute has changed. + * Example: Update sub-components. + */ update(): void { // Get sub-components. - // const sliderNav: TPSliderNavElement | null = this.querySelector( 'tp-slider-nav' ); - // const sliderCounts: NodeListOf | null = this.querySelectorAll( 'tp-slider-count' ); - const leftArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="previous"]' ); - const rightArrow: TPSliderArrowElement | null = this.getArrow( 'tp-slider-arrow[direction="next"]' ); + const leftArrow: TPCarouselArrowElement | null = this.getArrow( 'tp-carousel-arrow[direction="previous"]' ); + const rightArrow: TPCarouselArrowElement | null = this.getArrow( 'tp-carousel-arrow[direction="next"]' ); // Set active slide. - const slides: NodeListOf | null | undefined = this.getSlideElements(); + const slides: NodeListOf | null | undefined = this.getSlideElements(); // Check if slides are available. if ( slides ) { - slides.forEach( ( slide: TPSliderSlideElement, index: number ): void => { + slides.forEach( ( slide: TPCarouselSlideElement, index: number ): void => { // Update active attribute. if ( this.currentSlideIndex - 1 === index ) { slide.setAttribute( 'active', 'yes' ); @@ -273,39 +270,6 @@ export class TPSliderElement extends HTMLElement { } ); } - // First, set the template for the slider nav. - // sliderNav?.updateNavItems(); - - // Once the template has been set, query the slider nav items. - // const sliderNavItems: NodeListOf | null = this.querySelectorAll( 'tp-slider-nav-item' ); - - // Set current slider nav item. - // if ( sliderNavItems ) { - // sliderNavItems.forEach( ( navItem: TPSliderNavItemElement, index: number ): void => { - // // Update current attribute after considering step. - // if ( Math.ceil( this.currentSlideIndex / this.step ) - 1 === index ) { - // navItem.setAttribute( 'current', 'yes' ); - // } else { - // navItem.removeAttribute( 'current' ); - // } - // } ); - // } - - // Update slider count. - // if ( sliderCounts ) { - // // Set total attribute. - // this.setAttribute( 'total', this.getTotalSlides().toString() ); - - // // Update slider counts. - // sliderCounts.forEach( ( slideCount: TPSliderCountElement ) => { - // // Check if the slideCount.update is a function. - // if ( 'function' === typeof slideCount.update ) { - // // Update slide count. - // slideCount.update(); - // } - // } ); - // } - // Enable / disable arrows. if ( 'yes' !== this.getAttribute( 'infinite' ) ) { // For the last slide. @@ -321,11 +285,7 @@ export class TPSliderElement extends HTMLElement { } else { leftArrow?.removeAttribute( 'disabled' ); } - } - // else { - // rightArrow?.removeAttribute( 'disabled' ); - // leftArrow?.removeAttribute( 'disabled' ); - // } + } } /** @@ -335,10 +295,10 @@ export class TPSliderElement extends HTMLElement { */ getTotalSlides(): number { // To get the total number of slides. - const slides: NodeListOf | null | undefined = this.getSlideElements(); + const slides: NodeListOf | null | undefined = this.getSlideElements(); // Check if slides are available. - if (slides) { + if ( slides ) { // Tell the total number of slides. return slides.length; } @@ -352,35 +312,37 @@ export class TPSliderElement extends HTMLElement { * * @param {number} index Slide index. */ - setCurrentSlide(index: number): void { + setCurrentSlide( index: number ): void { // Check if slide index is valid. - if (index > this.getTotalSlides() || index <= 0) { + if ( index > this.getTotalSlides() || index <= 0 ) { // Stop! It's not valid. return; } // dispatch slide-set event. this.dispatchEvent( - new CustomEvent("slide-set", { + new CustomEvent( 'slide-set', { bubbles: true, detail: { slideIndex: index, }, - }), + } ), ); // Set current slide index. - this.setAttribute("current-slide", index.toString()); + this.setAttribute( 'current-slide', index.toString() ); } /** * Navigate to the next slide. */ next(): void { + // Flag that it is a programatic scroll. + this.flagProgramaticScroll(); + // Initialize total slides variable. const totalSlides: number = this.getTotalSlides(); - - + // Check if we are at the last slide considering per view attribute. if ( this.currentSlideIndex >= totalSlides - this.perView + 1 ) { // Check if we are in infinite mode. @@ -410,6 +372,9 @@ export class TPSliderElement extends HTMLElement { * Navigate to the previous slide. */ previous(): void { + // Flag that it is a programatic scroll. + this.flagProgramaticScroll(); + // Check if we are at the first slide. if ( this.currentSlideIndex <= 1 ) { // Check if we are in infinite mode. @@ -431,4 +396,18 @@ export class TPSliderElement extends HTMLElement { this.setCurrentSlide( 1 ); } } + + /** + * To set a flag if it is a programatic scroll. + */ + flagProgramaticScroll() { + // Set the flag to true. + this.isProgramaticScroll = true; + + // Set the flag to false after 500ms. + setTimeout( () => { + // Set the flag to false. + this.isProgramaticScroll = false; + }, 500 ); + } } diff --git a/src/new-slider/index.html b/src/new-slider/index.html deleted file mode 100644 index 08b12d5..0000000 --- a/src/new-slider/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - Web Component: Slider - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - diff --git a/src/new-slider/index.ts b/src/new-slider/index.ts deleted file mode 100644 index f6fc444..0000000 --- a/src/new-slider/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Styles. - */ -import './style.scss'; - -/** - * Components. - */ -import { TPSliderElement } from './tp-slider'; -import { TPSliderTrackElement } from './tp-slider-track'; -import { TPSliderSlidesElement } from './tp-slider-slides'; -import { TPSliderSlideElement } from './tp-slider-slide'; -import { TPSliderArrowElement } from './tp-slider-arrow'; - -/** - * Register Components. - */ -customElements.define( 'tp-slider', TPSliderElement ); -customElements.define( 'tp-slider-track', TPSliderTrackElement ); -customElements.define( 'tp-slider-slides', TPSliderSlidesElement ); -customElements.define( 'tp-slider-slide', TPSliderSlideElement ); -customElements.define( 'tp-slider-arrow', TPSliderArrowElement ); diff --git a/src/new-slider/slider-components.ts b/src/new-slider/slider-components.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/new-slider/tp-slider-arrow.ts b/src/new-slider/tp-slider-arrow.ts deleted file mode 100644 index 88afa09..0000000 --- a/src/new-slider/tp-slider-arrow.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Internal dependencies. - */ - -import { TPSliderElement } from "./tp-slider"; - -/** - * TP Slider Arrow. - */ -export class TPSliderArrowElement extends HTMLElement { - - - /** - * Constructor. - */ - constructor() { - - super(); - - // Get the button and add event listener. - this.querySelector( 'button' )?.addEventListener( 'click', this.handleClick.bind( this ) ); - - } - - /** - * Handle when the button is clicked. - */ - handleClick(): void { - // If disabled, do nothing. - if ( 'yes' === this.getAttribute( 'disabled' ) ) { - // Early return. - return; - } - - // Get the slider. - const slider: TPSliderElement | null = this.closest( 'tp-slider' ); - - // If no slider, early return. - if ( ! slider ) { - // Early return. - return; - } - - // Initiate slider according to the direction of the button clicked. - if ( 'previous' === this.getAttribute( 'direction' ) ) { - slider.previous(); - } else if ( 'next' === this.getAttribute( 'direction' ) ) { - slider.next(); - } - } - - -} diff --git a/src/new-slider/tp-slider-slide.ts b/src/new-slider/tp-slider-slide.ts deleted file mode 100644 index 4217a65..0000000 --- a/src/new-slider/tp-slider-slide.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Slider. - */ -export class TPSliderSlideElement extends HTMLElement { - - - /** - * Constructor. - */ - constructor() { - - super(); - - - } -} diff --git a/src/new-slider/tp-slider-slides.ts b/src/new-slider/tp-slider-slides.ts deleted file mode 100644 index 8a09a7f..0000000 --- a/src/new-slider/tp-slider-slides.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Slider. - */ -export class TPSliderSlidesElement extends HTMLElement { - - - /** - * Constructor. - */ - constructor() { - - super(); - - - } -} diff --git a/src/new-slider/tp-slider-track.ts b/src/new-slider/tp-slider-track.ts deleted file mode 100644 index d08890b..0000000 --- a/src/new-slider/tp-slider-track.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Slider. - */ -export class TPSliderTrackElement extends HTMLElement { - - - /** - * Constructor. - */ - constructor() { - - super(); - - - } -} diff --git a/webpack.config.js b/webpack.config.js index 37c1fa3..19cfb26 100755 --- a/webpack.config.js +++ b/webpack.config.js @@ -78,7 +78,7 @@ module.exports = ( env ) => { entry: { modal: './src/modal/index.ts', slider: './src/slider/index.ts', - newSlider: './src/new-slider/index.ts', + newSlider: './src/carousel/index.ts', tabs: './src/tabs/index.ts', form: './src/form/index.ts', accordion: './src/accordion/index.ts', From 7f61859e1d40853847cf3eba1991b6fe95f66b16 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Mon, 23 Jun 2025 20:15:51 +0530 Subject: [PATCH 30/31] WP-205 Add scroll snap to tp-slider with intersection observer --- src/carousel/style.scss | 2 - src/carousel/tp-carousel.ts | 2 +- src/slider/index.html | 32 ++++---- src/slider/style.scss | 4 + src/slider/tp-slider.ts | 144 +++++++++++++++++++----------------- 5 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/carousel/style.scss b/src/carousel/style.scss index 0cb669b..fd04f84 100644 --- a/src/carousel/style.scss +++ b/src/carousel/style.scss @@ -1,5 +1,3 @@ - - tp-carousel-slides { overflow-y: visible; overflow-x: auto; diff --git a/src/carousel/tp-carousel.ts b/src/carousel/tp-carousel.ts index ac2dcfa..2344008 100644 --- a/src/carousel/tp-carousel.ts +++ b/src/carousel/tp-carousel.ts @@ -22,7 +22,7 @@ export class TPCarouselElement extends HTMLElement { super(); this.slides = this.querySelectorAll( 'tp-carousel-slide' ); this.slideTrack = this.querySelector( 'tp-carousel-slides' ); - this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 0.999 } ); + this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 1 } ); // Set current slide. if ( ! this.getAttribute( 'current-slide' ) ) { diff --git a/src/slider/index.html b/src/slider/index.html index c53dc8a..29ddc3e 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -33,21 +33,17 @@
- + -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
@@ -63,7 +59,7 @@
- +
- +
- +
- + - +
- +
diff --git a/src/slider/style.scss b/src/slider/style.scss index f78544f..0ee4a62 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 530fa2c..311466a 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -15,9 +15,10 @@ export class TPSliderElement extends HTMLElement { /** * Properties. */ - protected touchStartX: number = 0; - protected touchStartY: number = 0; - protected swipeThreshold: number = 200; + protected isProgramaticScroll: boolean = false; + protected _observer: IntersectionObserver; + protected slideTrack: TPSliderSlidesElement | null; + protected slides: NodeListOf | null; protected responsiveSettings: { [ key: string ]: any }; protected allowedResponsiveKeys: string[] = [ 'flexible-height', @@ -36,20 +37,24 @@ export class TPSliderElement extends HTMLElement { constructor() { // Initialize parent. super(); + this.slides = this.querySelectorAll( 'tp-slider-slide' ); + this.slideTrack = this.querySelector( 'tp-slider-track' ); + this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 1, rootMargin: '0px 0px 90% 0px' } ); +// console.log(this.slides); // 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(); this.setAttribute( 'initialized', 'yes' ); + // Observe which slide is in view. + this.slides.forEach( ( slide ) => this._observer.observe( slide ) ); + // Responsive Settings. const responsiveSettingsJSON: string = this.getAttribute( 'responsive' ) || ''; this.responsiveSettings = responsiveSettingsJSON ? JSON.parse( responsiveSettingsJSON ) : []; @@ -65,10 +70,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 ) ); } /** @@ -105,6 +106,12 @@ export class TPSliderElement extends HTMLElement { attributeChangedCallback( name: string = '', oldValue: string = '', newValue: string = '' ): void { // Keep an eye on current slide. if ( 'current-slide' === name && oldValue !== newValue ) { + // console.log('in attribute changed callback'); + + // if( ! this.isProgramaticScroll ) { + // this.updateHeight(); + // } + this.slide(); this.dispatchEvent( new CustomEvent( 'slide-complete', { bubbles: true } ) ); } @@ -208,6 +215,9 @@ export class TPSliderElement extends HTMLElement { * Navigate to the next slide. */ next(): void { + // Flag that it is a programatic scroll. + this.flagProgramaticScroll(); + // Initialize total slides variable. const totalSlides: number = this.getTotalSlides(); @@ -240,6 +250,9 @@ export class TPSliderElement extends HTMLElement { * Navigate to the previous slide. */ previous(): void { + // Flag that it is a programatic scroll. + this.flagProgramaticScroll(); + // Check if we are at the first slide. if ( this.currentSlideIndex <= 1 ) { // Check if we are in infinite mode. @@ -296,12 +309,45 @@ export class TPSliderElement extends HTMLElement { this.setAttribute( 'current-slide', index.toString() ); } + /** + * Change current-slide attribute on scroll. + * + * @param {IntersectionObserverEntry[]} entries slides which enter or leave on scroll. + */ + attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { + console.log( 'attributeChangeOnScroll', entries ); + + // If the scroll is programatic. + if ( this.isProgramaticScroll ) { + // Do nothing. + return; + } + + // Change the current slide index when slide comes into view. + entries?.forEach( ( entry ) => { + // Check if the entry is intersecting with the slide track. + if ( entry.isIntersecting && entry.target instanceof TPSliderSlideElement && this.slides ) { + const index = Array.from( this.slides ).indexOf( entry.target ); + console.log( 'index', index, this.perView, this.currentSlideIndex ); + + // Update current slide index based on if it is is right or left scroll. + if ( index + 1 - ( this.perView - 1 ) > this.currentSlideIndex ) { + this.currentSlideIndex = index + 1 - ( this.perView - 1 ); + } else if ( index + 1 - ( this.perView - 1 ) < this.currentSlideIndex ) { + this.currentSlideIndex = index + 1; + } + } + } ); + } + /** * Slide to the current slide. * * @protected */ protected slide(): void { + // console.log("i am in slide"); + // Check if slider is disabled. if ( 'yes' === this.getAttribute( 'disabled' ) ) { // Yes, it is. So stop. @@ -329,7 +375,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', + } ); } } @@ -494,6 +543,7 @@ export class TPSliderElement extends HTMLElement { // Set the height of the container to be the max height of the slides in the current view. slidesContainer.style.height = `${ maxHeight }px`; } else { + // console.log("i am in update height"); // Set the height of the container to be the height of the current slide. const height: number = slides[ this.currentSlideIndex - 1 ].scrollHeight; slidesContainer.style.height = `${ height }px`; @@ -581,64 +631,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. */ @@ -669,4 +661,18 @@ export class TPSliderElement extends HTMLElement { this.dispatchEvent( new CustomEvent( 'auto-slide-complete' ) ); }, interval ); } + + /** + * To set a flag if it is a programatic scroll. + */ + flagProgramaticScroll() { + // Set the flag to true. + this.isProgramaticScroll = true; + + // Set the flag to false after 500ms. + setTimeout( () => { + // Set the flag to false. + this.isProgramaticScroll = false; + }, 500 ); + } } From 6a75ced9cad3a5d971149f070d3693a124c44b08 Mon Sep 17 00:00:00 2001 From: harshdeep-gill Date: Tue, 24 Jun 2025 09:20:59 +0530 Subject: [PATCH 31/31] WP-205 Revamped the logic without using intersection observer --- src/carousel/index.html | 64 ----- src/carousel/index.ts | 22 -- src/carousel/style.scss | 12 - src/carousel/tp-carousel-arrow.ts | 47 ---- src/carousel/tp-carousel-slide.ts | 8 - src/carousel/tp-carousel-slides.ts | 8 - src/carousel/tp-carousel-track.ts | 8 - src/carousel/tp-carousel.ts | 413 ----------------------------- src/slider/index.html | 24 +- src/slider/tp-slider.ts | 73 +++-- 10 files changed, 46 insertions(+), 633 deletions(-) delete mode 100644 src/carousel/index.html delete mode 100644 src/carousel/index.ts delete mode 100644 src/carousel/style.scss delete mode 100644 src/carousel/tp-carousel-arrow.ts delete mode 100644 src/carousel/tp-carousel-slide.ts delete mode 100644 src/carousel/tp-carousel-slides.ts delete mode 100644 src/carousel/tp-carousel-track.ts delete mode 100644 src/carousel/tp-carousel.ts diff --git a/src/carousel/index.html b/src/carousel/index.html deleted file mode 100644 index 095bb1e..0000000 --- a/src/carousel/index.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - Web Component: Slider - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - diff --git a/src/carousel/index.ts b/src/carousel/index.ts deleted file mode 100644 index ea94600..0000000 --- a/src/carousel/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Styles. - */ -import './style.scss'; - -/** - * Components. - */ -import { TPCarouselElement } from './tp-carousel'; -import { TPCarouselTrackElement } from './tp-carousel-track'; -import { TPCarouselSlidesElement } from './tp-carousel-slides'; -import { TPCarouselSlideElement } from './tp-carousel-slide'; -import { TPCarouselArrowElement } from './tp-carousel-arrow'; - -/** - * Register Components. - */ -customElements.define( 'tp-carousel', TPCarouselElement ); -customElements.define( 'tp-carousel-track', TPCarouselTrackElement ); -customElements.define( 'tp-carousel-slides', TPCarouselSlidesElement ); -customElements.define( 'tp-carousel-slide', TPCarouselSlideElement ); -customElements.define( 'tp-carousel-arrow', TPCarouselArrowElement ); diff --git a/src/carousel/style.scss b/src/carousel/style.scss deleted file mode 100644 index fd04f84..0000000 --- a/src/carousel/style.scss +++ /dev/null @@ -1,12 +0,0 @@ -tp-carousel-slides { - overflow-y: visible; - overflow-x: auto; - scroll-snap-type: x mandatory; - display: flex; - scrollbar-width: none; -} - -tp-carousel-slide { - flex: 0 0 100%; - scroll-snap-align: start; -} diff --git a/src/carousel/tp-carousel-arrow.ts b/src/carousel/tp-carousel-arrow.ts deleted file mode 100644 index 8ac4912..0000000 --- a/src/carousel/tp-carousel-arrow.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Internal dependencies. - */ -import { TPCarouselElement } from './tp-carousel'; - -/** - * TP Carousel Arrow. - */ -export class TPCarouselArrowElement extends HTMLElement { - /** - * Constructor. - */ - constructor() { - // Call the parent constructor. - super(); - - // Get the button and add event listener. - this.querySelector( 'button' )?.addEventListener( 'click', this.handleClick.bind( this ) ); - } - - /** - * Handle when the button is clicked. - */ - handleClick(): void { - // If disabled, do nothing. - if ( 'yes' === this.getAttribute( 'disabled' ) ) { - // Early return. - return; - } - - // Get the carousel. - const carousel: TPCarouselElement | null = this.closest( 'tp-carousel' ); - - // If no carousel, early return. - if ( ! carousel ) { - // Early return. - return; - } - - // Initiate carousel according to the direction of the button clicked. - if ( 'previous' === this.getAttribute( 'direction' ) ) { - carousel.previous(); - } else if ( 'next' === this.getAttribute( 'direction' ) ) { - carousel.next(); - } - } -} diff --git a/src/carousel/tp-carousel-slide.ts b/src/carousel/tp-carousel-slide.ts deleted file mode 100644 index 0d96c38..0000000 --- a/src/carousel/tp-carousel-slide.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Carousel. - */ -export class TPCarouselSlideElement extends HTMLElement {} diff --git a/src/carousel/tp-carousel-slides.ts b/src/carousel/tp-carousel-slides.ts deleted file mode 100644 index 86fee93..0000000 --- a/src/carousel/tp-carousel-slides.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Carousel. - */ -export class TPCarouselSlidesElement extends HTMLElement {} diff --git a/src/carousel/tp-carousel-track.ts b/src/carousel/tp-carousel-track.ts deleted file mode 100644 index 7383b9f..0000000 --- a/src/carousel/tp-carousel-track.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Internal dependencies. - */ - -/** - * TP Carousel. - */ -export class TPCarouselTrackElement extends HTMLElement {} diff --git a/src/carousel/tp-carousel.ts b/src/carousel/tp-carousel.ts deleted file mode 100644 index 2344008..0000000 --- a/src/carousel/tp-carousel.ts +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Internal dependencies. - */ -import { TPCarouselArrowElement } from './tp-carousel-arrow'; -import { TPCarouselSlideElement } from './tp-carousel-slide'; -import { TPCarouselSlidesElement } from './tp-carousel-slides'; - -/** - * TP Carousel. - */ -export class TPCarouselElement extends HTMLElement { - protected _observer: IntersectionObserver; - protected slides: NodeListOf | null; - protected slideTrack: TPCarouselSlidesElement | null; - protected isProgramaticScroll: boolean = false; - - /** - * Constructor. - */ - constructor() { - // Initialize parent. - super(); - this.slides = this.querySelectorAll( 'tp-carousel-slide' ); - this.slideTrack = this.querySelector( 'tp-carousel-slides' ); - this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 1 } ); - - // Set current slide. - if ( ! this.getAttribute( 'current-slide' ) ) { - this.setAttribute( 'current-slide', '1' ); - } - - // Initialize carousel. - this.slide(); - this.setAttribute( 'initialized', 'yes' ); - - // Observe which slide is in view. - this.slides.forEach( ( slide ) => this._observer.observe( slide ) ); - } - - /** - * Connected callback. - */ - connectedCallback() { - // Update on initial render. - this.update(); - } - - /** - * Change current-slide attribute on scroll. - * - * @param {IntersectionObserverEntry[]} entries slides which enter or leave on scroll. - */ - attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { - // If the scroll is programatic. - if ( this.isProgramaticScroll ) { - // Do nothing. - return; - } - - // Change the current slide index when slide comes into view. - entries?.forEach( ( entry ) => { - // Check if the entry is intersecting with the slide track. - if ( entry.isIntersecting && entry.target instanceof TPCarouselSlideElement && this.slides ) { - const index = Array.from( this.slides ).indexOf( entry.target ); - - // Update current slide index based on if it is is right or left scroll. - if ( index + 1 - ( this.perView - 1 ) > this.currentSlideIndex ) { - this.currentSlideIndex = index + 1 - ( this.perView - 1 ); - } else if ( index + 1 - ( this.perView - 1 ) < this.currentSlideIndex ) { - this.currentSlideIndex = index + 1; - } - } - } ); - } - - /** - * Get observed attributes. - * - * @return {Array} List of observed attributes. - */ - static get observedAttributes(): string[] { - // Observed attributes. - return [ 'current-slide', 'infinite', 'per-view' ]; - } - - /** - * Attribute changed callback. - * - * @param {string} name Attribute name. - * @param {string} oldValue Old value. - * @param {string} newValue New value. - */ - attributeChangedCallback( name: string = '', oldValue: string = '', newValue: string = '' ): void { - // Keep an eye on current slide. - if ( 'current-slide' === name && oldValue !== newValue ) { - this.slide(); - this.dispatchEvent( new CustomEvent( 'slide-complete', { bubbles: true } ) ); - } - - // Update the component after the attribute change. - this.update(); - } - - /** - * Get current slide index. - * - * @return {number} Current slide index. - */ - get currentSlideIndex(): number { - // To get the current slide index. - return parseInt( this.getAttribute( 'current-slide' ) ?? '1' ); - } - - /** - * Set current slide index. - * - * @param {number} index Slide index. - */ - set currentSlideIndex( index: number ) { - // Set the current slide index. - this.setCurrentSlide( index ); - } - - /** - * Get per view. - * - * @return {number} Current step. - */ - get perView(): number { - // To get number of slides per view. - return parseInt( this.getAttribute( 'per-view' ) ?? '1' ); - } - - /** - * Set per view. - * - * @param {number} perView Per view. - */ - set perView( perView: number ) { - // Set the number of slides per view. - this.setAttribute( 'per-view', perView.toString() ); - } - - /** - * Get current step. - * - * @return {number} Current step. - */ - get step(): number { - // To get the current step. - return parseInt( this.getAttribute( 'step' ) ?? '1' ); - } - - /** - * Set current step. - * - * @param {number} step Step. - */ - set step( step: number ) { - // Set the current step. - this.setAttribute( 'step', step.toString() ); - } - - /** - * Get Slide Elements. - */ - getSlideElements() { - // Get slides. - const slidesElement: TPCarouselSlidesElement | null = this.querySelector( 'tp-carousel-slides' ); - const slides: NodeListOf | null | undefined = slidesElement?.querySelectorAll( ':scope > tp-carousel-slide' ); - - // Return array of slides. - return slides; - } - - /** - * Slide to the current slide. - * - * @protected - */ - protected slide(): void { - // Check if carousel is disabled. - if ( 'yes' === this.getAttribute( 'disabled' ) ) { - // Yes, it is. So stop. - return; - } - - // Get slides. - const slidesContainer: TPCarouselSlidesElement | null = this.querySelector( 'tp-carousel-slides' ); - const slides: NodeListOf | null | undefined = this.getSlideElements(); - - // Check if we have slide container and slides. - if ( ! slidesContainer || ! slides ) { - // No, we don't. Either one of them or both are missing. So stop. - return; - } - - // Check if behaviour is set to fade and slide on the current slide index is present in the slides array. - if ( slides[ this.currentSlideIndex - 1 ] ) { - // Yes, it is. So slide to the current slide. - slidesContainer.scroll( { - left: slides[ this.currentSlideIndex - 1 ].offsetLeft - slides[ 0 ].offsetLeft, - behavior: 'smooth', - } ); - } - } - - /** - * Get the arrow element by selector. - * - * In case of nested carousels, it difficult to find the correct arrow - * because arrows can be placed anywhere. - * This function checks if the parent tp-carousel belongs to this component, - * then return that arrow element, using 'this'. - * - * @param {string} selector Selector. - */ - getArrow( selector: string ) { - // Get all arrows. - const arrows: NodeListOf | null = this.querySelectorAll( selector ); - const parentCarouselElement: TPCarouselElement = this; - let theArrow: TPCarouselArrowElement | null = this.querySelector( selector ); - - // Loop through all the arrows including the one's inside nested carousel. - arrows.forEach( ( arrow ) => { - /** - * If the closest tp-carousel is the same as the parentCarouselElement, that means we have found - * the correct arrow. - */ - if ( parentCarouselElement === arrow.closest( 'tp-carousel' ) ) { - theArrow = arrow; - } - } ); - - // Return arrow. - return theArrow; - } - - /** - * Get current slide index. - * - * @return {number} Current slide index. - */ - getCurrentSlide(): number { - // Get current slide index. - return this.currentSlideIndex; - } - - /** - * Update stuff when any attribute has changed. - * Example: Update sub-components. - */ - update(): void { - // Get sub-components. - const leftArrow: TPCarouselArrowElement | null = this.getArrow( 'tp-carousel-arrow[direction="previous"]' ); - const rightArrow: TPCarouselArrowElement | null = this.getArrow( 'tp-carousel-arrow[direction="next"]' ); - - // Set active slide. - const slides: NodeListOf | null | undefined = this.getSlideElements(); - - // Check if slides are available. - if ( slides ) { - slides.forEach( ( slide: TPCarouselSlideElement, index: number ): void => { - // Update active attribute. - if ( this.currentSlideIndex - 1 === index ) { - slide.setAttribute( 'active', 'yes' ); - } else { - slide.removeAttribute( 'active' ); - } - } ); - } - - // Enable / disable arrows. - if ( 'yes' !== this.getAttribute( 'infinite' ) ) { - // For the last slide. - if ( this.getCurrentSlide() === this.getTotalSlides() + 1 ) { - rightArrow?.setAttribute( 'disabled', 'yes' ); - } else { - rightArrow?.removeAttribute( 'disabled' ); - } - - // For the first slide. - if ( 1 === this.getCurrentSlide() ) { - leftArrow?.setAttribute( 'disabled', 'yes' ); - } else { - leftArrow?.removeAttribute( 'disabled' ); - } - } - } - - /** - * Get total number of slides. - * - * @return {number} Total slides. - */ - getTotalSlides(): number { - // To get the total number of slides. - const slides: NodeListOf | null | undefined = this.getSlideElements(); - - // Check if slides are available. - if ( slides ) { - // Tell the total number of slides. - return slides.length; - } - - // Else return 0. - return 0; - } - - /** - * Set the current slide index. - * - * @param {number} index Slide index. - */ - setCurrentSlide( index: number ): void { - // Check if slide index is valid. - if ( index > this.getTotalSlides() || index <= 0 ) { - // Stop! It's not valid. - return; - } - - // dispatch slide-set event. - this.dispatchEvent( - new CustomEvent( 'slide-set', { - bubbles: true, - detail: { - slideIndex: index, - }, - } ), - ); - - // Set current slide index. - this.setAttribute( 'current-slide', index.toString() ); - } - - /** - * Navigate to the next slide. - */ - next(): void { - // Flag that it is a programatic scroll. - this.flagProgramaticScroll(); - - // Initialize total slides variable. - const totalSlides: number = this.getTotalSlides(); - - // Check if we are at the last slide considering per view attribute. - if ( this.currentSlideIndex >= totalSlides - this.perView + 1 ) { - // Check if we are in infinite mode. - if ( 'yes' === this.getAttribute( 'infinite' ) ) { - // Yes, we are, and go back to first slide. - this.setCurrentSlide( 1 ); - } - - // Terminate. - return; - } - - // Get next slide index by adding minimum of step or remaining number of slides. - const nextSlideIndex: number = this.currentSlideIndex + Math.min( this.step, totalSlides - this.currentSlideIndex - this.perView + 1 ); - - // Check if the next slide step is not taking it beyond the last slide. - if ( nextSlideIndex > ( totalSlides - this.perView + 1 ) ) { - // Yes, it is. - return; - } - - // Everything is good, go to next slide. - this.setCurrentSlide( nextSlideIndex ); - } - - /** - * Navigate to the previous slide. - */ - previous(): void { - // Flag that it is a programatic scroll. - this.flagProgramaticScroll(); - - // Check if we are at the first slide. - if ( this.currentSlideIndex <= 1 ) { - // Check if we are in infinite mode. - if ( 'yes' === this.getAttribute( 'infinite' ) ) { - this.setCurrentSlide( this.getTotalSlides() - this.perView + 1 ); - } - - // Terminate. - return; - } - - // Get previous slide index. - const previousSlideNumber: number = this.currentSlideIndex; - - // Check if the previous slide step is not taking it beyond the first slide. - if ( previousSlideNumber > this.step ) { - this.setCurrentSlide( previousSlideNumber - this.step ); - } else { - this.setCurrentSlide( 1 ); - } - } - - /** - * To set a flag if it is a programatic scroll. - */ - flagProgramaticScroll() { - // Set the flag to true. - this.isProgramaticScroll = true; - - // Set the flag to false after 500ms. - setTimeout( () => { - // Set the flag to false. - this.isProgramaticScroll = false; - }, 500 ); - } -} diff --git a/src/slider/index.html b/src/slider/index.html index 29ddc3e..915895c 100644 --- a/src/slider/index.html +++ b/src/slider/index.html @@ -59,7 +59,7 @@
- +

- +

- +
- + - +
- +
diff --git a/src/slider/tp-slider.ts b/src/slider/tp-slider.ts index 311466a..34ff1f9 100644 --- a/src/slider/tp-slider.ts +++ b/src/slider/tp-slider.ts @@ -16,8 +16,9 @@ export class TPSliderElement extends HTMLElement { * Properties. */ protected isProgramaticScroll: boolean = false; - protected _observer: IntersectionObserver; - protected slideTrack: TPSliderSlidesElement | null; + // protected _observer: IntersectionObserver; + protected slidesTrack: TPSliderSlidesElement | null; + protected slidesTrackRect: DOMRect | undefined; protected slides: NodeListOf | null; protected responsiveSettings: { [ key: string ]: any }; protected allowedResponsiveKeys: string[] = [ @@ -38,9 +39,10 @@ export class TPSliderElement extends HTMLElement { // Initialize parent. super(); this.slides = this.querySelectorAll( 'tp-slider-slide' ); - this.slideTrack = this.querySelector( 'tp-slider-track' ); - this._observer = new IntersectionObserver( this.attributeChangeOnScroll?.bind( this ), { root: this.slideTrack, threshold: 1, rootMargin: '0px 0px 90% 0px' } ); -// console.log(this.slides); + this.slidesTrack = this.querySelector( 'tp-slider-slides' ); + this.slidesTrackRect = this.slidesTrack?.getBoundingClientRect(); + + this.slidesTrack?.addEventListener( 'scroll', this.handleCurrentSlideOnScroll.bind( this ) ); // Set current slide. if ( ! this.getAttribute( 'current-slide' ) ) { @@ -52,9 +54,6 @@ export class TPSliderElement extends HTMLElement { this.autoSlide(); this.setAttribute( 'initialized', 'yes' ); - // Observe which slide is in view. - this.slides.forEach( ( slide ) => this._observer.observe( slide ) ); - // Responsive Settings. const responsiveSettingsJSON: string = this.getAttribute( 'responsive' ) || ''; this.responsiveSettings = responsiveSettingsJSON ? JSON.parse( responsiveSettingsJSON ) : []; @@ -86,6 +85,33 @@ export class TPSliderElement extends HTMLElement { this.update(); } + /** + * 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() { + if ( ! this.slidesTrack || ! this.slides ) { + return; + } + + const isAtRightEnd = Math.abs(this.slidesTrack.scrollLeft + this.slidesTrack.clientWidth - this.slidesTrack.scrollWidth) < 1; + + if ( isAtRightEnd ) { + // If the current slide index is equal to the total number of slides, set it to the last slide. + this.setCurrentSlide( this.slides.length ); + } else { + this.slides?.forEach( ( slide: TPSliderSlideElement, index: number ) => { + const slideRect = slide.getBoundingClientRect(); + + if ( this.slidesTrackRect && slideRect?.left - this.slidesTrackRect.left === 0 ) { + this.setCurrentSlide( index + 1 ); + } + } ); + } + } + /** * Get observed attributes. * @@ -309,37 +335,6 @@ export class TPSliderElement extends HTMLElement { this.setAttribute( 'current-slide', index.toString() ); } - /** - * Change current-slide attribute on scroll. - * - * @param {IntersectionObserverEntry[]} entries slides which enter or leave on scroll. - */ - attributeChangeOnScroll( entries: IntersectionObserverEntry[] ): void { - console.log( 'attributeChangeOnScroll', entries ); - - // If the scroll is programatic. - if ( this.isProgramaticScroll ) { - // Do nothing. - return; - } - - // Change the current slide index when slide comes into view. - entries?.forEach( ( entry ) => { - // Check if the entry is intersecting with the slide track. - if ( entry.isIntersecting && entry.target instanceof TPSliderSlideElement && this.slides ) { - const index = Array.from( this.slides ).indexOf( entry.target ); - console.log( 'index', index, this.perView, this.currentSlideIndex ); - - // Update current slide index based on if it is is right or left scroll. - if ( index + 1 - ( this.perView - 1 ) > this.currentSlideIndex ) { - this.currentSlideIndex = index + 1 - ( this.perView - 1 ); - } else if ( index + 1 - ( this.perView - 1 ) < this.currentSlideIndex ) { - this.currentSlideIndex = index + 1; - } - } - } ); - } - /** * Slide to the current slide. *