UNPKG

ngx-carousel-ease

Version:

ngx-carousel-ease is a versatile Angular library providing a feature-rich, simple, and performant carousel component. This library supports infinite and responsive mode, mouse and touch event. Attention has been put to accessibility, performance, and frie

1 lines 102 kB
{"version":3,"file":"ngx-carousel-ease.mjs","sources":["../../../projects/carousel/src/lib/carousel.ts","../../../projects/carousel/src/lib/validation.ts","../../../projects/carousel/src/lib/communSlider.ts","../../../projects/carousel/src/lib/infiniteSlider.ts","../../../projects/carousel/src/lib/finiteSlider.ts","../../../projects/carousel/src/lib/carousel.service.ts","../../../projects/carousel/src/lib/carousel.component.ts","../../../projects/carousel/src/lib/carousel.component.html","../../../projects/carousel/src/public-api.ts","../../../projects/carousel/src/ngx-carousel-ease.ts"],"sourcesContent":["import { signal } from '@angular/core';\r\n\r\nexport class Carousel {\r\n slides!: NodeListOf<HTMLDivElement>;\r\n originalSlideWidth!: number;\r\n slideWidthWithGap!: number;\r\n\r\n numberDots = signal(0);\r\n maxScrollableContent = signal(0);\r\n arrayNumberDots = signal<number[]>([]);\r\n\r\n slidesContainer!: HTMLDivElement;\r\n totalSlides = 0;\r\n initialSlideToShow = 1;\r\n paddingCarousel = 0;\r\n carouselWidth!: number;\r\n widthSlideContainer!: number;\r\n arrayOfSlides: HTMLDivElement[] = [];\r\n\r\n constructor(\r\n private readonly carousel: HTMLDivElement,\r\n private readonly maxWidthCarousel: number | undefined,\r\n public slideToShow: number,\r\n public slideWidth: number,\r\n public readonly slideMaxWidth: number,\r\n public readonly gap: number,\r\n private readonly responsive: boolean,\r\n private readonly loop: boolean\r\n ) {\r\n this.init();\r\n }\r\n\r\n get carouselElement() {\r\n return this.carousel;\r\n }\r\n\r\n init() {\r\n this.paddingCarousel = this.getPaddingCarousel();\r\n this.initialSlideToShow = this.slideToShow;\r\n this.slidesContainer = this.selectSlidesContainer();\r\n this.slides = this.selectSlides();\r\n this.arrayOfSlides = this.slidesToArray();\r\n this.totalSlides = this.slides.length;\r\n this.originalSlideWidth = this.slideWidth;\r\n this.setWidthSlides();\r\n this.setMaxWidthCarousel();\r\n this.updateProperties();\r\n this.slidesContainer.style.gap = `${this.gap}px`;\r\n this.setDraggableImgToFalse();\r\n }\r\n\r\n /**\r\n * Set the slide width and max width\r\n * NB: In responsive mode, width is automatically adapted through the setAutoColumnSlideContainer() method.\r\n *\r\n */\r\n setWidthSlides() {\r\n for (const slide of this.slides) {\r\n slide.style.maxWidth = `${this.slideMaxWidth}px`;\r\n slide.style.width = '100%';\r\n slide.style.userSelect = 'none';\r\n\r\n if (!this.responsive) {\r\n slide.style.width = `${this.slideWidth}px`;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Update carousel properties\r\n * Occurs at start and at resizing.\r\n */\r\n updateProperties() {\r\n this.carouselWidth = this.carousel.clientWidth;\r\n if (this.responsive) {\r\n this.updateSlideToShowResponsive();\r\n this.setAutoColumnSlideContainer();\r\n } else {\r\n this.updateSlideToShowNotResponsive();\r\n }\r\n\r\n this.numberDots.set(this.setNumberDots());\r\n this.arrayNumberDots.set([...Array(this.numberDots()).keys()]);\r\n\r\n this.slideWidthWithGap = this.slideWidth + this.gap;\r\n this.setWidthSlideContainer();\r\n this.maxScrollableContent.set(this.getMaxScroll());\r\n }\r\n\r\n /**\r\n * Update slide to show (responsive mode)\r\n * Computes the number of slide fitting\r\n */\r\n updateSlideToShowResponsive() {\r\n const slideWidthPlusGap = this.originalSlideWidth + this.gap;\r\n\r\n const referenceWidth = Math.min(\r\n this.carouselWidth,\r\n this.maxWidthCarousel || Infinity,\r\n window.innerWidth\r\n );\r\n\r\n const slideFitting = Math.floor(referenceWidth / slideWidthPlusGap) || 1;\r\n\r\n this.determineSlideToShow(slideFitting);\r\n }\r\n\r\n /**\r\n * Determine slide to show\r\n * Useful to compute the slide displayed on screen and for the slider class.\r\n * In not responsive mode, the maximum slides to show is determined by the maximum available space and the width of the slide set by the user.\r\n */\r\n determineSlideToShow(slideFitting: number) {\r\n const maxSlidesToShow = this.responsive\r\n ? this.initialSlideToShow\r\n : this.totalSlides;\r\n\r\n this.slideToShow = Math.min(slideFitting, maxSlidesToShow);\r\n }\r\n\r\n /**\r\n * Update slide to show (not responsive mode)\r\n * Computes the number of slide fitting. The number of slides to be shown are determined by the number of slides fitting within its container.\r\n */\r\n updateSlideToShowNotResponsive() {\r\n const slideWidthPlusGap = this.slideWidth + this.gap;\r\n\r\n const referenceWidth = Math.min(\r\n this.carouselWidth,\r\n this.maxWidthCarousel || Infinity,\r\n window.innerWidth\r\n );\r\n\r\n const numberOfSlidesComputed =\r\n Math.floor(referenceWidth / slideWidthPlusGap) || 1;\r\n const slideFitting = Math.min(numberOfSlidesComputed, this.totalSlides);\r\n\r\n this.determineSlideToShow(slideFitting);\r\n }\r\n\r\n getPaddingCarousel() {\r\n const computedStyle = window.getComputedStyle(this.carousel);\r\n const paddingLeft = computedStyle.getPropertyValue('padding-left');\r\n const paddingRight = computedStyle.getPropertyValue('padding-right');\r\n\r\n return parseFloat(paddingLeft) + parseFloat(paddingRight);\r\n }\r\n\r\n setMaxWidthCarousel() {\r\n this.carousel.style.maxWidth = `${this.maxWidthCarousel}px`;\r\n }\r\n\r\n /**\r\n * Set the with of a slide, responsive mode\r\n * There are [n cards - 1] gaps (3 cards, 2 gaps)\r\n */\r\n setAutoColumnSlideContainer() {\r\n const windowWidth =\r\n this.carouselWidth -\r\n this.paddingCarousel -\r\n (this.slideToShow - 1) * this.gap;\r\n\r\n const widthPerSlide = windowWidth / this.slideToShow;\r\n this.slidesContainer.style.gridAutoColumns = `${widthPerSlide}px`;\r\n\r\n this.slideWidth = widthPerSlide;\r\n }\r\n\r\n /**\r\n * Define the slide container width\r\n * Make non visible gaps of non visible cards scrollable and is used to compute the maxScrollableContent (strechingEffect)\r\n */\r\n setWidthSlideContainer() {\r\n this.widthSlideContainer =\r\n this.selectSlides().length * this.slideWidthWithGap - this.gap;\r\n this.slidesContainer.style.width = `${this.widthSlideContainer}px`;\r\n }\r\n\r\n selectSlides(): NodeListOf<HTMLDivElement> {\r\n return this.carousel.querySelectorAll('.carousel-slide');\r\n }\r\n\r\n slidesToArray() {\r\n return Array.from(this.slides);\r\n }\r\n\r\n selectSlidesContainer() {\r\n return this.carousel.querySelector('.slides-container') as HTMLDivElement;\r\n }\r\n\r\n /**\r\n * Set number of dots\r\n * If infinite mode, one more window than normal mode.\r\n */\r\n setNumberDots() {\r\n if (this.loop) return this.totalSlides;\r\n\r\n return this.slideToShow > 1\r\n ? this.totalSlides - this.slideToShow + 1\r\n : this.totalSlides;\r\n }\r\n\r\n /**\r\n * Get the max scrollable content\r\n * Useful for the streching effect (not infinite mode), end of the slides\r\n */\r\n getMaxScroll() {\r\n return (this.numberDots() - 1) * this.slideWidthWithGap;\r\n }\r\n\r\n setDraggableImgToFalse() {\r\n const images = this.slidesContainer.querySelectorAll('img');\r\n images?.forEach((image) => {\r\n image.setAttribute('draggable', 'false');\r\n });\r\n }\r\n}\r\n","export class Validation {\r\n carouselSlides!: NodeListOf<HTMLDivElement> | undefined;\r\n\r\n constructor(\r\n private readonly carousel: HTMLDivElement,\r\n private readonly slideWidth: number,\r\n private readonly slideMaxWidth: number,\r\n private readonly gap: number,\r\n private readonly slideToScroll: number\r\n ) {\r\n this.carouselSlides = this.carousel.querySelectorAll('.carousel-slide');\r\n this.slideMaxWidthShouldBeGreaterThanSlideWidth();\r\n this.slideWidthAndGapShouldBeGreaterThanZero();\r\n this.requiredClassShouldBeAdded();\r\n this.slideToScrollNotGreaterThanTotalSlides();\r\n }\r\n\r\n slideMaxWidthShouldBeGreaterThanSlideWidth() {\r\n if (this.slideMaxWidth < this.slideWidth) {\r\n throw new Error(\r\n `slideMaxWidth (value: ${this.slideMaxWidth}) is lower than slideWidth (value: ${this.slideWidth}). Please increase the max width or decrease the slide width.`\r\n );\r\n }\r\n }\r\n\r\n slideWidthAndGapShouldBeGreaterThanZero() {\r\n const slideWidthPlusGap = this.slideWidth + this.gap;\r\n\r\n if (slideWidthPlusGap <= 0) {\r\n throw new Error(\r\n 'Unable to construct Carousel. SlideWidth and gap lower or equal than zero. Please add a positive value for the slideWidth and gap.'\r\n );\r\n }\r\n }\r\n\r\n requiredClassShouldBeAdded() {\r\n if (this.carouselSlides === undefined || this.carouselSlides.length === 0) {\r\n throw new Error(\r\n 'No elements with \"carousel-slide\" as class have been found. Please add this class to each of your slides.'\r\n );\r\n }\r\n }\r\n\r\n slideToScrollNotGreaterThanTotalSlides() {\r\n if (\r\n this.carouselSlides &&\r\n this.slideToScroll > this.carouselSlides.length\r\n ) {\r\n throw new Error(\r\n `slideToScroll value (${this.slideToScroll}) is greater than the total amount of slide (${this.carouselSlides.length}). This can cause invisible cards in infinite mode. Please lower the slideToScroll value.`\r\n );\r\n }\r\n }\r\n}\r\n","import { ChangeDetectorRef, signal } from '@angular/core';\r\nimport { Carousel } from './carousel';\r\nimport { CarouselService } from './carousel.service';\r\n\r\nexport class CommunSlider {\r\n dragging = signal(false);\r\n currentSlide = signal(0);\r\n lastWindow = 0;\r\n currentTranslation = signal(0);\r\n previousTranslation = 0;\r\n direction: 'right' | 'left' = 'right';\r\n startX = 0;\r\n previousX = 0;\r\n currentX = 0;\r\n positionChange = 0;\r\n prevLimit = 0;\r\n nextLimit = 0;\r\n slidesContainer!: HTMLDivElement;\r\n arrayOfSlides!: HTMLDivElement[];\r\n totalAmountOfSlides!: number;\r\n fullWidthInf = 0;\r\n lastWindowTranslation = 0;\r\n totalSlides = 0;\r\n DOMLimitReached = false;\r\n visibleOffsetCardNotResponsive = 0;\r\n invisibleOffsetCardNotResponsive = 0;\r\n accumulatedSlide = 0;\r\n currentCarouselID = 0;\r\n autoInterval!: number;\r\n playActive = signal(false);\r\n playButtonDisabled = signal(false);\r\n directionAutoPlay = (slides: number) => {};\r\n\r\n constructor(\r\n readonly carousel: Carousel,\r\n readonly responsive: boolean,\r\n readonly slideToScroll: number,\r\n readonly LIMIT_AUTO_SLIDE: number,\r\n readonly strechingLimit: number,\r\n readonly autoSlide: boolean,\r\n readonly animationTimingFn: string,\r\n readonly animationTimingMs: number,\r\n readonly enableMouseDrag: boolean,\r\n readonly enableTouch: boolean,\r\n readonly autoPlay: boolean,\r\n readonly autoPlayInterval: number,\r\n readonly autoPlayAtStart: boolean,\r\n readonly playDirection: string,\r\n readonly autoplaySlideToScroll: number,\r\n public carouselService: CarouselService,\r\n public cd: ChangeDetectorRef\r\n ) {\r\n this.initProperties();\r\n this.updateProperties();\r\n }\r\n\r\n initProperties() {\r\n this.slidesContainer = this.carousel.slidesContainer;\r\n this.arrayOfSlides = this.carousel.arrayOfSlides;\r\n\r\n this.totalSlides = this.carousel.totalSlides;\r\n this.totalAmountOfSlides = this.totalSlides;\r\n\r\n this.nextLimit = Math.floor(this.carousel.slideWidthWithGap);\r\n this.prevLimit = -this.carousel.slideWidthWithGap;\r\n\r\n if (this.autoPlayAtStart) this.launchAutoPlay();\r\n }\r\n\r\n launchAutoPlay() {\r\n if (!this.autoPlay) return;\r\n\r\n this.playActive.set(true);\r\n\r\n this.autoInterval = window.setInterval(() => {\r\n this.directionAutoPlay(this.autoplaySlideToScroll);\r\n this.cd.markForCheck();\r\n }, this.autoPlayInterval);\r\n }\r\n\r\n stopAutoPlay() {\r\n if (!this.autoPlay) return;\r\n\r\n this.playActive.set(false);\r\n clearInterval(this.autoInterval);\r\n }\r\n\r\n /**\r\n * Update properties of the slider\r\n * Fired at start and at resizing.\r\n */\r\n updateProperties() {\r\n this.updateNotResponsive();\r\n\r\n this.lastWindow = this.carousel.numberDots() - 1;\r\n this.fullWidthInf = this.totalSlides * this.carousel.slideWidthWithGap;\r\n\r\n this.lastWindowTranslation =\r\n this.slidesContainer.clientWidth -\r\n this.carousel.slideToShow * this.carousel.slideWidthWithGap +\r\n this.carousel.gap;\r\n\r\n this.lastWindowTranslation += this.responsive\r\n ? 0\r\n : -this.visibleOffsetCardNotResponsive - this.carousel.gap;\r\n\r\n const maxScrollable =\r\n this.carousel.widthSlideContainer -\r\n this.carousel.carouselWidth +\r\n this.carousel.paddingCarousel;\r\n this.carousel.maxScrollableContent.set(maxScrollable);\r\n\r\n if (this.carousel.numberDots() === 1) {\r\n // if all slides visible in one window, max scrollable content equals 0\r\n this.carousel.maxScrollableContent.set(0);\r\n }\r\n }\r\n\r\n updateNotResponsive() {\r\n if (this.responsive) return;\r\n\r\n // visible part of the offset of the card in px\r\n this.visibleOffsetCardNotResponsive =\r\n this.carousel.carouselWidth -\r\n this.carousel.slideToShow * this.carousel.slideWidthWithGap -\r\n this.carousel.paddingCarousel;\r\n\r\n this.invisibleOffsetCardNotResponsive =\r\n this.carousel.slideWidth - this.visibleOffsetCardNotResponsive;\r\n }\r\n\r\n /**\r\n * Fired at drag start\r\n * Instantiate property of the starting drag point on the X axis. Used to compute the translation.\r\n * Disabling the transition while applying the transformation because of the attached animation.\r\n */\r\n dragStart(event: MouseEvent | TouchEvent) {\r\n if (this.currentEventIsDisabled(event)) return;\r\n clearInterval(this.autoInterval);\r\n\r\n this.dragging.set(true);\r\n this.startX =\r\n event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;\r\n\r\n // Useful for direction detection\r\n this.previousX = this.startX;\r\n this.slidesContainer.style.transition = 'none';\r\n }\r\n\r\n /**\r\n * Checks if current event is allowed by user.\r\n * TouchEvent partially supported on Firefox (and working on Safari despite the MDN docs).\r\n */\r\n currentEventIsDisabled(event: MouseEvent | TouchEvent) {\r\n const isMouseEvent = event instanceof MouseEvent;\r\n\r\n return (\r\n (isMouseEvent && !this.enableMouseDrag) ||\r\n (!isMouseEvent && !this.enableTouch)\r\n );\r\n }\r\n\r\n /**\r\n * Update the direction\r\n * Do not update the direction in case of the same previous position.\r\n */\r\n setDirection() {\r\n if (this.previousX > this.currentX) {\r\n this.direction = 'right';\r\n } else if (this.previousX < this.currentX) {\r\n this.direction = 'left';\r\n }\r\n }\r\n\r\n /**\r\n * Update the last window translation\r\n * Useful to get the max translation at the end of the slides.\r\n * In not responsive mode, there is possibly a not fully displayed card (card offset).\r\n */\r\n updateLastWindowTranslation() {\r\n const total = this.totalAmountOfSlides * this.carousel.slideWidthWithGap;\r\n\r\n this.lastWindowTranslation =\r\n total - this.carousel.slideToShow * this.carousel.slideWidthWithGap;\r\n\r\n this.lastWindowTranslation += this.responsive\r\n ? 0\r\n : -this.visibleOffsetCardNotResponsive - this.carousel.gap;\r\n }\r\n\r\n /**\r\n * Decrease limit (movement to the left)\r\n * In infinite mode, take full width of a set if on the first slide as new slides are created to the left (a whole set offset).\r\n * Exception: if not responsive (card offset) and finite carousel, the next limit is at the maximum (the end of the carousel)\r\n */\r\n decreaseLimits(slidesCreatedOnTheLeft = false) {\r\n let translationCorrectionAfterClone = this.prevLimit;\r\n\r\n if (slidesCreatedOnTheLeft) {\r\n translationCorrectionAfterClone = this.fullWidthInf;\r\n }\r\n\r\n this.prevLimit =\r\n translationCorrectionAfterClone - this.carousel.slideWidthWithGap;\r\n\r\n this.nextLimit = this.prevLimit + 2 * this.carousel.slideWidthWithGap;\r\n\r\n this.prevLimit = Math.floor(this.prevLimit);\r\n this.nextLimit = Math.floor(this.nextLimit);\r\n }\r\n\r\n /**\r\n * Increase limit on basis of previous computed limits (movement to the right)\r\n * Schema: || previous | current || next\r\n * Exception: if not responsive (card offset) and finite carousel, the next limit is at the maximum (the end of the carousel)\r\n */\r\n increaseLimits() {\r\n this.nextLimit += Math.floor(this.carousel.slideWidthWithGap);\r\n this.prevLimit = Math.floor(\r\n this.nextLimit - this.carousel.slideWidthWithGap * 2\r\n );\r\n\r\n // console.log(this.prevLimit, this.nextLimit);\r\n }\r\n\r\n /**\r\n * Change prev and next limit on basis of the provided slide number\r\n * Prev and next limit are always calculated as the following:\r\n * || <= prev | current || <= next\r\n */\r\n changePrevAndNextLimits(slideNumber: number) {\r\n const limitInPX = slideNumber * this.carousel.slideWidthWithGap;\r\n this.nextLimit = Math.floor(limitInPX + this.carousel.slideWidthWithGap);\r\n\r\n this.prevLimit = Math.floor(\r\n this.nextLimit - this.carousel.slideWidthWithGap * 2\r\n );\r\n\r\n // console.log(this.prevLimit, this.nextLimit);\r\n }\r\n\r\n fireSlideChangeEvent(slideHasChanged: boolean, slide: number) {\r\n if (!slideHasChanged) return;\r\n\r\n this.carouselService.emit(slide, this.carousel.carouselElement);\r\n }\r\n\r\n applyTransformation(transformation: number) {\r\n this.slidesContainer.style.transition = `transform ${this.animationTimingMs}ms ${this.animationTimingFn}`;\r\n\r\n this.applyTranslation(-transformation);\r\n\r\n this.dragging.set(false);\r\n this.previousTranslation = -transformation;\r\n this.currentTranslation.set(-transformation);\r\n }\r\n\r\n applyTranslation(transformation: number) {\r\n this.slidesContainer.style.transform = `translate3d(${transformation}px, 0, 0)`;\r\n }\r\n}\r\n","import { ChangeDetectorRef } from '@angular/core';\r\nimport { Carousel } from './carousel';\r\nimport { CarouselService } from './carousel.service';\r\nimport { CommunSlider } from './communSlider';\r\n\r\nexport class InfiniteSlider extends CommunSlider {\r\n readonly MAX_DOM_SIZE: number;\r\n\r\n constructor(\r\n carousel: Carousel,\r\n responsive: boolean,\r\n slideToScroll: number,\r\n LIMIT_AUTO_SLIDE: number,\r\n strechingLimit: number,\r\n autoSlide: boolean,\r\n animationTimingFn: string,\r\n animationTimingMs: number,\r\n MAX_DOM_SIZE: number,\r\n enableMouseDrag: boolean,\r\n enableTouch: boolean,\r\n autoPlay: boolean,\r\n autoPlayInterval: number,\r\n autoPlayAtStart: boolean,\r\n playDirection: string,\r\n autoplaySlideToScroll: number,\r\n carouselService: CarouselService,\r\n cd: ChangeDetectorRef\r\n ) {\r\n super(\r\n carousel,\r\n responsive,\r\n slideToScroll,\r\n LIMIT_AUTO_SLIDE,\r\n strechingLimit,\r\n autoSlide,\r\n animationTimingFn,\r\n animationTimingMs,\r\n enableMouseDrag,\r\n enableTouch,\r\n autoPlay,\r\n autoPlayInterval,\r\n autoPlayAtStart,\r\n playDirection,\r\n autoplaySlideToScroll,\r\n carouselService,\r\n cd\r\n );\r\n this.MAX_DOM_SIZE = MAX_DOM_SIZE;\r\n this.initProperties();\r\n this.updateProperties();\r\n this.addSlidesToRightAtStart();\r\n }\r\n\r\n override initProperties() {\r\n this.defineAutoPlayDirection();\r\n super.initProperties();\r\n }\r\n\r\n /**\r\n * next and prev are defined separatively\r\n */\r\n defineAutoPlayDirection() {\r\n if (this.playDirection === 'ltr') {\r\n this.directionAutoPlay = this.next.bind(this);\r\n } else {\r\n this.directionAutoPlay = this.prev.bind(this);\r\n }\r\n }\r\n\r\n /**\r\n * Add slides to the right at start\r\n * If only one window (number of dots === 1) and not responsive mode, there is possibly space at start for slides to the right (even though this configuration does not make a lot of sense)\r\n * TODO: ajouter des slides si slidesToShow > total slides\r\n */\r\n addSlidesToRightAtStart() {\r\n if (this.carousel.numberDots() > 1 || this.responsive) {\r\n return;\r\n }\r\n\r\n this.addSlidesToTheRight();\r\n }\r\n\r\n /**\r\n * Fired at drag start\r\n * Instantiate property of the starting drag point on the X axis. Used to compute the translation.\r\n * Disabling the transition while applying the transformation because of the attached animation.\r\n */\r\n override dragStart(event: MouseEvent | TouchEvent) {\r\n if (super.currentEventIsDisabled(event)) return;\r\n\r\n super.dragStart(event);\r\n }\r\n\r\n /**\r\n * Relaunch autoPlay if not disabled or infinite mode (never disabled by limits)\r\n * Restart only if started (playActive).\r\n */\r\n relaunchAutoPlay() {\r\n if (this.playActive()) {\r\n clearInterval(this.autoInterval);\r\n this.launchAutoPlay();\r\n }\r\n }\r\n\r\n /**\r\n * Fired at drag end\r\n */\r\n dragStop(event: MouseEvent | TouchEvent) {\r\n if (this.currentEventIsDisabled(event)) return;\r\n this.dragging.set(false);\r\n this.previousTranslation = this.currentTranslation();\r\n\r\n this.autoSlider();\r\n }\r\n\r\n autoSlider() {\r\n if (!this.autoSlide) return;\r\n\r\n const referenceWidth = Math.min(\r\n this.carousel.slideWidth,\r\n this.carousel.slideMaxWidth || Infinity\r\n );\r\n const currentLimit = this.prevLimit + this.carousel.slideWidthWithGap;\r\n\r\n // previousTranslation always a negative number, currentLimit always positive\r\n const currentPositionChange = this.previousTranslation + currentLimit;\r\n const moveComparedToSlide = (currentPositionChange / referenceWidth) * 100;\r\n\r\n if (\r\n moveComparedToSlide < -this.LIMIT_AUTO_SLIDE ||\r\n moveComparedToSlide > this.LIMIT_AUTO_SLIDE\r\n ) {\r\n if (moveComparedToSlide > this.LIMIT_AUTO_SLIDE) {\r\n this.changeSlideNumber(-1);\r\n this.decreaseLimits();\r\n } else {\r\n this.changeSlideNumber(1);\r\n this.increaseLimits();\r\n }\r\n }\r\n\r\n if (-this.currentTranslation() > this.lastWindowTranslation) {\r\n this.addSlidesToTheRight();\r\n }\r\n\r\n this.computeTransformation(this.accumulatedSlide);\r\n }\r\n\r\n /**\r\n * Fired at dragging\r\n * Compute the translation, change the slide number, update the direction.\r\n */\r\n dragMove(event: MouseEvent | TouchEvent) {\r\n if (this.currentEventIsDisabled(event)) return;\r\n if (!this.dragging()) return;\r\n\r\n this.currentX =\r\n event instanceof MouseEvent ? event.pageX : event.changedTouches[0].pageX;\r\n\r\n this.setDirection();\r\n this.previousX = this.currentX;\r\n\r\n this.positionChange = this.currentX - this.startX;\r\n this.currentTranslation.set(this.positionChange + this.previousTranslation);\r\n\r\n this.applyTranslation(this.currentTranslation());\r\n this.modifyCurrentSlide();\r\n }\r\n\r\n /**\r\n * Responsible for changing slide number and updating the limits.\r\n * If createSlidesInfiniteModeIfLimitsReached() doesn't take action, slide change according to previous computed limits.\r\n */\r\n modifyCurrentSlide() {\r\n if (this.createSlidesInfiniteModeIfLimits()) return;\r\n\r\n if (-this.currentTranslation() < this.prevLimit) {\r\n this.changeSlideNumber(-1);\r\n this.decreaseLimits();\r\n } else if (-this.currentTranslation() >= this.nextLimit) {\r\n this.changeSlideNumber(1);\r\n this.increaseLimits();\r\n }\r\n }\r\n\r\n /**\r\n * Handle slide creation in infinite mode if limits reached\r\n * Mouse or touch drag.\r\n */\r\n createSlidesInfiniteModeIfLimits() {\r\n if (this.currentTranslation() > 0) {\r\n this.addSlidesToTheLeft();\r\n this.decreaseLimits(true);\r\n\r\n // not enabled at start\r\n if (this.currentSlide() > 0) {\r\n this.changeSlideNumber(-1);\r\n }\r\n return true;\r\n } else if (-this.currentTranslation() > this.lastWindowTranslation) {\r\n this.addSlidesToTheRight();\r\n\r\n if (this.DOMLimitReached) {\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Append or prepend new slides according to the direction\r\n * If new slides prepended, update the translation to the correct place (appending new slides do not change the translation).\r\n * Limit DOM growth or update last window translation if applicable.\r\n */\r\n appendOrPrependNElements() {\r\n if (this.direction === 'left') {\r\n for (let i = this.arrayOfSlides.length - 1; i >= 0; i--) {\r\n const clonedElement = this.arrayOfSlides[i].cloneNode(true);\r\n this.slidesContainer.prepend(clonedElement);\r\n }\r\n\r\n this.accumulatedSlide += this.totalSlides;\r\n this.resetViewLeftDirection();\r\n } else {\r\n for (let i = 0; i < this.arrayOfSlides.length; i++) {\r\n const clonedElement = this.arrayOfSlides[i].cloneNode(true);\r\n this.slidesContainer.append(clonedElement);\r\n }\r\n }\r\n\r\n if (this.totalAmountOfSlides >= this.MAX_DOM_SIZE * this.totalSlides) {\r\n // Limit DOM growth, max X times original DOM\r\n // console.log('dom limit reached');\r\n this.limitDOMGrowth();\r\n this.DOMLimitReached = true;\r\n } else {\r\n this.totalAmountOfSlides += this.totalSlides;\r\n this.DOMLimitReached = false;\r\n this.updateLastWindowTranslation();\r\n }\r\n }\r\n\r\n /**\r\n * Limit DOM growth\r\n * Reset the view accordingly.\r\n */\r\n limitDOMGrowth() {\r\n const slides = this.carousel.selectSlides();\r\n\r\n if (this.direction === 'right') {\r\n for (let i = 0; i < this.totalSlides; i++) {\r\n slides[i].remove();\r\n }\r\n\r\n this.resetViewRightDirection();\r\n this.accumulatedSlide -= this.totalSlides;\r\n } else {\r\n for (\r\n let i = slides.length - 1;\r\n i > slides.length - this.totalSlides - 1;\r\n i--\r\n ) {\r\n slides[i].remove();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Reset the view in a movement to the left\r\n * New slides added to the left, so the view has to be updated accordingly.\r\n * If the carousel is moved with the mouse | touch event (dragging is true), the offset should be equal to a full carousel width. Otherwise (with the buttons), the computed translation should be taken into account.\r\n * Side comment: the view does not have to be updated for slides added to the right, since relative order does not change.\r\n * getBoundingClientRect triggers reflow of the element.\r\n */\r\n resetViewLeftDirection() {\r\n const translation = Math.abs(this.previousTranslation) + this.fullWidthInf;\r\n this.previousTranslation = -translation;\r\n\r\n this.slidesContainer.style.transition = 'none';\r\n const transformation = this.dragging() ? -this.fullWidthInf : -translation;\r\n this.applyTranslation(transformation);\r\n\r\n this.slidesContainer.getBoundingClientRect();\r\n }\r\n\r\n /**\r\n * Reset the view in a movement to the right\r\n * First set of slides have been deleted, so the translation has to be updated accordingly.\r\n * currentTranslation is a negative number so it will be decreased by a set of slides.\r\n * getBoundingClientRect triggers reflow of the element.\r\n */\r\n resetViewRightDirection() {\r\n const translation = this.currentTranslation() + this.fullWidthInf;\r\n\r\n this.slidesContainer.style.transition = 'none';\r\n this.applyTranslation(translation);\r\n this.previousTranslation = translation - this.positionChange;\r\n\r\n this.slidesContainer.getBoundingClientRect();\r\n }\r\n\r\n addSlidesToTheLeft() {\r\n this.appendOrPrependNElements();\r\n }\r\n\r\n addSlidesToTheRight() {\r\n this.appendOrPrependNElements();\r\n }\r\n\r\n /**\r\n * Increase limit on basis of previous computed limits (movement to the right)\r\n * Schema: || previous | current || next\r\n */\r\n override increaseLimits() {\r\n this.nextLimit += Math.floor(this.carousel.slideWidthWithGap);\r\n this.prevLimit = Math.floor(\r\n this.nextLimit - this.carousel.slideWidthWithGap * 2\r\n );\r\n }\r\n\r\n /**\r\n * Previous button navigation\r\n */\r\n prev(slides = this.slideToScroll) {\r\n this.direction = 'left';\r\n\r\n this.handleBtnInfinite(-slides);\r\n this.changeSlideNumber(-slides);\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n this.computeTransformation(this.accumulatedSlide);\r\n this.relaunchAutoPlay();\r\n }\r\n\r\n /**\r\n * Next button navigation\r\n */\r\n next(slides = this.slideToScroll) {\r\n this.direction = 'right';\r\n\r\n this.handleBtnInfinite(slides);\r\n this.changeSlideNumber(slides);\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n this.computeTransformation(this.accumulatedSlide);\r\n this.relaunchAutoPlay();\r\n }\r\n\r\n /**\r\n * Buttons navigation in infinite mode\r\n * Create new slide if limits reached (start or end). Update slide, limits and apply transformation accordingly.\r\n */\r\n handleBtnInfinite(step: number) {\r\n let cardOffset = 0;\r\n const goingTo = this.accumulatedSlide + step;\r\n\r\n // there is (possibly) a card offset if not responsive\r\n if (!this.responsive) cardOffset = 1;\r\n\r\n if (goingTo < 0) {\r\n this.addSlidesToTheLeft();\r\n } else if (\r\n goingTo + this.carousel.slideToShow + cardOffset >\r\n this.totalAmountOfSlides\r\n ) {\r\n this.addSlidesToTheRight();\r\n }\r\n }\r\n\r\n /**\r\n * Navigation with bullet points\r\n * Update values accordingly.\r\n */\r\n goTo(bullet: number) {\r\n const slideHasChanged = this.currentSlide() !== bullet;\r\n this.direction = this.currentSlide() < bullet ? 'right' : 'left';\r\n\r\n this.currentSlide.set(bullet);\r\n this.fireSlideChangeEvent(slideHasChanged, this.currentSlide());\r\n\r\n this.relaunchAutoPlay();\r\n this.navInfiniteBullets(bullet);\r\n }\r\n\r\n /**\r\n * Bullets navigation\r\n * Create new slides if exceeding end of carousel.\r\n */\r\n navInfiniteBullets(bullet: number) {\r\n let cardOffset = 0;\r\n const positionOfCurrentSlide = this.accumulatedSlide % this.totalSlides;\r\n const difference = bullet - positionOfCurrentSlide;\r\n this.accumulatedSlide += difference;\r\n\r\n // there is (possibly) a card offset if not responsive\r\n if (!this.responsive) cardOffset = 1;\r\n\r\n if (\r\n this.accumulatedSlide + this.carousel.slideToShow + cardOffset >\r\n this.totalAmountOfSlides\r\n ) {\r\n this.addSlidesToTheRight();\r\n }\r\n\r\n this.computeTransformation(this.accumulatedSlide);\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n }\r\n\r\n /**\r\n * Exception: if only one window (numberDots === 1), update the accumulatedSlide to let the transformation occurs, but currentSlide should stay at 0.\r\n */\r\n changeSlideNumber(step: number) {\r\n const slideNumberBeforeChange: number = this.currentSlide();\r\n this.infiniteChangeSlideNumber(step);\r\n\r\n if (this.carousel.numberDots() === 1) {\r\n this.currentSlide.set(0);\r\n }\r\n\r\n const current: number =\r\n this.carousel.numberDots() > 1\r\n ? this.currentSlide()\r\n : this.accumulatedSlide;\r\n\r\n const slideHasChanged: boolean =\r\n slideNumberBeforeChange !== this.currentSlide();\r\n this.fireSlideChangeEvent(slideHasChanged, current);\r\n }\r\n\r\n infiniteChangeSlideNumber(step: number) {\r\n this.accumulatedSlide += step;\r\n this.currentSlide.update((slide) => slide + step);\r\n\r\n const lastSlide = this.totalSlides - 1;\r\n if (this.currentSlide() > lastSlide) {\r\n this.currentSlide.update((slide) => slide % this.totalSlides);\r\n } else if (this.currentSlide() < 0) {\r\n this.currentSlide.update((slide) => slide + this.totalSlides);\r\n }\r\n }\r\n\r\n /**\r\n * Compute transformation that will be applied on basis of the provided slide number\r\n */\r\n computeTransformation(slide: number) {\r\n const transformation = slide * this.carousel.slideWidthWithGap;\r\n\r\n this.applyTransformation(transformation);\r\n }\r\n}\r\n","import { ChangeDetectorRef } from '@angular/core';\r\nimport { Carousel } from './carousel';\r\nimport { CarouselService } from './carousel.service';\r\nimport { CommunSlider } from './communSlider';\r\n\r\nexport class FiniteSlider extends CommunSlider {\r\n constructor(\r\n carousel: Carousel,\r\n responsive: boolean,\r\n slideToScroll: number,\r\n LIMIT_AUTO_SLIDE: number,\r\n strechingLimit: number,\r\n autoSlide: boolean,\r\n animationTimingFn: string,\r\n animationTimingMs: number,\r\n enableMouseDrag: boolean,\r\n enableTouch: boolean,\r\n autoPlay: boolean,\r\n autoPlayInterval: number,\r\n autoPlayAtStart: boolean,\r\n playDirection: string,\r\n autoplaySlideToScroll: number,\r\n carouselService: CarouselService,\r\n cd: ChangeDetectorRef\r\n ) {\r\n super(\r\n carousel,\r\n responsive,\r\n slideToScroll,\r\n LIMIT_AUTO_SLIDE,\r\n strechingLimit,\r\n autoSlide,\r\n animationTimingFn,\r\n animationTimingMs,\r\n enableMouseDrag,\r\n enableTouch,\r\n autoPlay,\r\n autoPlayInterval,\r\n autoPlayAtStart,\r\n playDirection,\r\n autoplaySlideToScroll,\r\n carouselService,\r\n cd\r\n );\r\n this.initProperties();\r\n this.updateProperties();\r\n }\r\n\r\n override initProperties() {\r\n this.defineAutoPlayDirection();\r\n super.initProperties();\r\n this.disableAutoPlayBtn();\r\n }\r\n\r\n /**\r\n * In finite carousel, clear autoPlay interval if limits are reached.\r\n */\r\n disableAutoPlayBtn() {\r\n if (!this.autoPlay) return;\r\n this.playButtonDisabled.set(false);\r\n\r\n // start\r\n if (\r\n this.currentSlide() === 0 &&\r\n this.playDirection === 'rtl' &&\r\n this.currentTranslation() === 0\r\n ) {\r\n this.playButtonDisabled.set(true);\r\n this.playActive.set(false);\r\n clearInterval(this.autoInterval);\r\n }\r\n\r\n // end\r\n if (\r\n this.currentSlide() === this.lastWindow &&\r\n this.playDirection === 'ltr' &&\r\n this.currentTranslation() <= -this.carousel.maxScrollableContent()\r\n ) {\r\n this.playButtonDisabled.set(true);\r\n this.playActive.set(false);\r\n clearInterval(this.autoInterval);\r\n }\r\n\r\n if (this.carousel.numberDots() === 1) this.playButtonDisabled.set(true);\r\n }\r\n\r\n /**\r\n * Fired at drag start\r\n * Instantiate property of the starting drag point on the X axis. Used to compute the translation.\r\n * Disabling the transition while applying the transformation because of the attached animation.\r\n */\r\n override dragStart(event: MouseEvent | TouchEvent) {\r\n super.dragStart(event);\r\n this.disableAutoPlayBtn();\r\n }\r\n\r\n /**\r\n * Fired at drag end\r\n * If limits reached (start or end), put back to the current slide. Updates previous translation.\r\n */\r\n dragStop(event: MouseEvent | TouchEvent) {\r\n if (this.currentEventIsDisabled(event)) return;\r\n this.dragging.set(false);\r\n this.previousTranslation = this.currentTranslation();\r\n\r\n this.autoSlider();\r\n this.disableAutoPlayBtn();\r\n\r\n const limit =\r\n this.currentTranslation() > 0 ||\r\n -this.currentTranslation() > this.lastWindowTranslation;\r\n\r\n if (limit) {\r\n this.computeTransformation(this.currentSlide());\r\n }\r\n }\r\n\r\n /**\r\n * Fired at dragging\r\n * Compute the translation, change the slide number, update the direction.\r\n */\r\n dragMove(event: MouseEvent | TouchEvent) {\r\n if (this.currentEventIsDisabled(event)) return;\r\n if (!this.dragging()) return;\r\n\r\n this.currentX =\r\n event instanceof MouseEvent ? event.pageX : event.changedTouches[0].pageX;\r\n\r\n this.setDirection();\r\n this.previousX = this.currentX;\r\n\r\n this.positionChange = this.currentX - this.startX;\r\n this.currentTranslation.set(this.positionChange + this.previousTranslation);\r\n\r\n // Current translation exceeding start or end limits\r\n if (this.strechingEffect()) return;\r\n\r\n this.applyTranslation(this.currentTranslation());\r\n this.modifyCurrentSlide();\r\n }\r\n\r\n strechingEffect() {\r\n return (\r\n (this.currentTranslation() > this.strechingLimit &&\r\n this.currentSlide() === 0) ||\r\n this.currentTranslation() <\r\n -this.carousel.maxScrollableContent() - this.strechingLimit\r\n );\r\n }\r\n\r\n /**\r\n * Responsible for changing slide number and updating the limits.\r\n * In finite mode, if all slides visible on one window or end of carousel, early return to not trigger change event.\r\n */\r\n modifyCurrentSlide() {\r\n if (this.endOfCarousel()) return;\r\n\r\n if (-this.currentTranslation() < this.prevLimit) {\r\n this.changeSlideNumber(-1);\r\n this.decreaseLimits();\r\n } else if (-this.currentTranslation() >= this.nextLimit) {\r\n this.changeSlideNumber(1);\r\n this.increaseLimits();\r\n }\r\n }\r\n\r\n endOfCarousel() {\r\n return (\r\n this.carousel.numberDots() === 1 ||\r\n (this.currentSlide() === this.lastWindow && this.direction === 'right')\r\n );\r\n }\r\n\r\n /**\r\n * Auto slide card if option enabled, applied on both directions.\r\n * Prevents auto slide on limits in finite mode (if streching < limit auto slide).\r\n * If only one slide is displayed (slideToShow === 1), the width of the slide corresponds to the window's width (a dot). Hence, taking the min between the two.\r\n * In non responsive and non infinite, there is possibly an offset of the current limit.\r\n */\r\n autoSlider() {\r\n if (!this.autoSlide) return;\r\n if (\r\n this.currentTranslation() > 0 ||\r\n -this.currentTranslation() > this.lastWindowTranslation\r\n ) {\r\n return;\r\n }\r\n\r\n const referenceWidth = Math.min(\r\n this.carousel.slideWidth,\r\n this.carousel.slideMaxWidth || Infinity\r\n );\r\n let currentLimit = this.prevLimit + this.carousel.slideWidthWithGap;\r\n\r\n if (!this.responsive && this.currentSlide() > this.lastWindow - 1) {\r\n currentLimit = this.lastWindowTranslation;\r\n }\r\n\r\n // previousTranslation always a negative number, currentLimit always positive\r\n const currentPositionChange = this.previousTranslation + currentLimit;\r\n const moveComparedToSlide = (currentPositionChange / referenceWidth) * 100;\r\n\r\n if (\r\n moveComparedToSlide < -this.LIMIT_AUTO_SLIDE ||\r\n moveComparedToSlide > this.LIMIT_AUTO_SLIDE\r\n ) {\r\n if (moveComparedToSlide > this.LIMIT_AUTO_SLIDE) {\r\n this.changeSlideNumber(-1);\r\n this.decreaseLimits();\r\n } else {\r\n this.changeSlideNumber(1);\r\n this.increaseLimits();\r\n }\r\n }\r\n\r\n this.computeTransformation(this.accumulatedSlide);\r\n }\r\n\r\n /**\r\n * Decrease limit (movement to the left)\r\n * Exception: if not responsive (card offset) and finite carousel, the next limit is at the maximum (the end of the carousel)\r\n */\r\n override decreaseLimits() {\r\n super.decreaseLimits();\r\n\r\n if (!this.responsive && this.currentSlide() >= this.lastWindow - 1) {\r\n this.nextLimit = this.lastWindowTranslation;\r\n }\r\n\r\n this.nextLimit = Math.floor(this.nextLimit);\r\n }\r\n\r\n /**\r\n * Increase limit on basis of previous computed limits (movement to the right)\r\n * Schema: || previous | current || next\r\n * Exception: if not responsive (card offset) and finite carousel, the next limit is at the maximum (the end of the carousel)\r\n */\r\n override increaseLimits() {\r\n super.increaseLimits();\r\n\r\n if (!this.responsive && this.currentSlide() >= this.lastWindow - 1) {\r\n this.nextLimit = this.lastWindowTranslation;\r\n\r\n if (this.currentSlide() === this.lastWindow) {\r\n // only update previous limit if last slide reached\r\n this.prevLimit = Math.floor(\r\n this.nextLimit - this.invisibleOffsetCardNotResponsive\r\n );\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Previous button navigation\r\n */\r\n prev(slides = this.slideToScroll) {\r\n this.direction = 'left';\r\n\r\n this.changeSlideNumber(-slides);\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n this.computeTransformation(this.accumulatedSlide);\r\n this.disableAutoPlayBtn();\r\n this.relaunchAutoPlay();\r\n }\r\n\r\n /**\r\n * Relaunch autoPlay if not disabled.\r\n * Restart only if started (playActive). Play button disabled when limits reached (finite carousel).\r\n */\r\n relaunchAutoPlay() {\r\n if (!this.playButtonDisabled() && this.playActive()) {\r\n clearInterval(this.autoInterval);\r\n this.launchAutoPlay();\r\n }\r\n }\r\n\r\n defineAutoPlayDirection() {\r\n if (this.playDirection === 'ltr') {\r\n this.directionAutoPlay = this.next.bind(this);\r\n } else {\r\n this.directionAutoPlay = this.prev.bind(this);\r\n }\r\n }\r\n\r\n /**\r\n * Next button navigation\r\n */\r\n next(slides = this.slideToScroll) {\r\n this.direction = 'right';\r\n\r\n this.changeSlideNumber(slides);\r\n this.changePrevAndNextLimits(this.accumulatedSlide);\r\n this.computeTransformation(this.accumulatedSlide);\r\n this.disableAutoPlayBtn();\r\n this.relaunchAutoPlay();\r\n }\r\n\r\n /**\r\n * Navigation with bullet points\r\n * Update values accordingly.\r\n */\r\n goTo(bullet: number) {\r\n const slideHasChanged = this.currentSlide() !== bullet;\r\n this.direction = this.currentSlide() < bullet ? 'right' : 'left';\r\n\r\n this.currentSlide.set(bullet);\r\n this.fireSlideChangeEvent(slideHasChanged, this.currentSlide());\r\n\r\n this.disableAutoPlayBtn();\r\n this.relaunchAutoPlay();\r\n\r\n this.accumulatedSlide = this.currentSlide();\r\n this.changePrevAndNextLimits(bullet);\r\n this.computeTransformation(bullet);\r\n }\r\n\r\n /**\r\n * Exception: if only one window (numberDots === 1), update the accumulatedSlide to let the transformation occurs, but currentSlide should stay at 0.\r\n */\r\n changeSlideNumber(step: number) {\r\n const slideNumberBeforeChange = this.currentSlide();\r\n\r\n this.finiteChangeSlideNumber(step);\r\n\r\n if (this.carousel.numberDots() === 1) {\r\n this.currentSlide.set(0);\r\n }\r\n\r\n const current =\r\n this.carousel.numberDots() > 1\r\n ? this.currentSlide()\r\n : this.accumulatedSlide;\r\n\r\n const slideHasChanged = slideNumberBeforeChange !== this.currentSlide();\r\n\r\n this.fireSlideChangeEvent(slideHasChanged, current);\r\n }\r\n\r\n finiteChangeSlideNumber(step: number) {\r\n this.currentSlide.update((slide) => slide + step);\r\n\r\n if (this.currentSlide() > this.lastWindow) {\r\n this.currentSlide.set(this.lastWindow);\r\n } else if (this.currentSlide() < 0) {\r\n this.currentSlide.set(0);\r\n }\r\n\r\n this.accumulatedSlide = this.currentSlide();\r\n }\r\n\r\n /**\r\n * Compute transformation that will be applied on basis of the provided slide number\r\n * Exception: if not responsive (card offset) and finite carousel, the next limit is the end of the carousel\r\n */\r\n computeTransformation(slide: number) {\r\n let transformation = slide * this.carousel.slideWidthWithGap;\r\n\r\n if (\r\n !this.responsive &&\r\n slide >= this.lastWindow - 1 &&\r\n this.carousel.numberDots() > 1\r\n ) {\r\n this.nextLimit = this.lastWindowTranslation;\r\n\r\n if (slide === this.lastWindow) {\r\n // if last window, go to maximum & update prev limit\r\n transformation = this.lastWindowTranslation;\r\n this.prevLimit = Math.floor(\r\n this.nextLimit - this.invisibleOffsetCardNotResponsive\r\n );\r\n }\r\n }\r\n\r\n this.applyTransformation(transformation);\r\n }\r\n}\r\n","import { Injectable } from '@angular/core';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class CarouselService {\r\n emit(slide: number, carouselElement: HTMLDivElement) {\r\n const event = new CustomEvent('slideChange', { detail: slide });\r\n\r\n // dispatch event on the parent\r\n carouselElement.parentElement?.dispatchEvent(event);\r\n }\r\n}\r\n","import {\r\n AfterViewInit,\r\n ChangeDetectionStrategy,\r\n ChangeDetectorRef,\r\n Component,\r\n computed,\r\n ElementRef,\r\n Inject,\r\n input,\r\n PLATFORM_ID,\r\n Signal,\r\n signal,\r\n ViewChild,\r\n} from '@angular/core';\r\nimport { Carousel } from './carousel';\r\nimport { CommonModule, isPlatformBrowser } from '@angular/common';\r\nimport { Validation } from './validation';\r\nimport { CarouselService } from './carousel.service';\r\nimport { AnimationsTiming } from './interfaces';\r\nimport { InfiniteSlider } from './infiniteSlider';\r\nimport { FiniteSlider } from './finiteSlider';\r\n\r\n@Component({\r\n selector: 'carousel',\r\n templateUrl: './carousel.component.html',\r\n styleUrls: ['./carousel.component.css'],\r\n imports: [CommonModule],\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class CarouselComponent implements AfterViewInit {\r\n maxWidthCarousel = input<number>();\r\n infinite = input(false);\r\n responsive = input(true);\r\n autoSlide = input(true);\r\n slideToShow = input(3);\r\n slideToScroll = input(2);\r\n autoslideLimitPercentCard = input(30);\r\n strechingLimit = input(60);\r\n slideWidth = input(300);\r\n slideMaxWidth = input(500);\r\n dots = input(true);\r\n arrows = input(true);\r\n counter = input(true);\r\n enableMouseDrag = input(true);\r\n enableTouch = input(true);\r\n counterSeparator = input('/');\r\n gapBetweenSlides = input(16);\r\n animationTimingMs = input(300);\r\n maxDomSize = input(4);\r\n animationTimingFn = input<AnimationsTiming>('ease-out');\r\n\r\n autoPlay = input(false);\r\n autoPlayInterval = input(1500);\r\n autoPlayAtStart = input(false);\r\n displayAutoPlayControls = input(true);\r\n autoPlaySlideToScroll = input(1);\r\n autoPlayDirection = input<'ltr' | 'rtl'>('ltr');\r\n\r\n @ViewChild('carouselContainer')\r\n private carouselContainer!: ElementRef<HTMLDivElement>;\r\n private resizeEvent!: () => void;\r\n private mouseUpEvent!: (event: MouseEvent | TouchEvent) => void;\r\n private visibilityEvent!: (event: any) => void;\r\n private previousWidth = 0;\r\n private currentWidth = 0;\r\n private isBrowser = true;\r\n private carousel!: Carousel;\r\n slider!: InfiniteSlider | FiniteSlider;\r\n\r\n // Template variables\r\n carouselDots: Signal<number> = signal(0);\r\n carouselMaxScrollableContent: Signal<number> = signal(0);\r\n carouselArrayNumberDots: Signal<number[]> = signal<number[]>([]);\r\n sliderCurrentSlide: Signal<number> = signal(0);\r\n sliderCurrentTranslation: Signal<number> = signal(0);\r\n sliderPlayActive: Signal<boolean> = signal(false);\r\n sliderPlayButtonDisabled: Signal<boolean> = signal(false);\r\n sliderDragging: Signal<boolean> = signal(false);\r\n\r\n constructor(\r\n private cd: ChangeDetectorRef,\r\n private carouselService: CarouselService,\r\n @Inject(PLATFORM_ID) platformId: Object\r\n ) {\r\n this.isBrowser = isPlatformBrowser(platformId);\r\n }\r\n\r\n ngAfterViewInit() {\r\n if (!this.isBrowser) return;\r\n\r\n const carouselContainer = this.carouselContainer.nativeElement;\r\n this.previousWidth = window.innerWidth;\r\n this.currentWidth = window.innerWidth;\r\n\r\n new Validation(\r\n carouselContainer,\r\n this.slideWidth(),\r\n this.slideMaxWidth(),\r\n this.gapBetweenSlides(),\r\n this.slideToScroll()\r\n );\r\n\r\n this.carousel = new Carousel(\r\n carouselContainer,\r\n this.maxWidthCarousel(),\r\n this.slideToShow(),\r\n this.slideWidth(),\r\n this.slideMaxWidth(),\r\n this.gapBetweenSlides(),\r\n this.responsive(),\r\n this.infinite()\r\n );\r\n\r\n this.instantiateSlider();\r\n this.listeners();\r\n this.cd.detectChanges();\r\n }\r\n\r\n instantiateSlider() {\r\n if (this.infinite()) {\r\n this.slider = new InfiniteSlider(\r\n this.carousel,\r\n this.responsive(),\r\n this.slideToScroll(),\r\n this.autoslideLimitPercentCard(),\r\n this.strechingLimit(),\r\n this.autoSlide(),\r\n this.animationTimingFn(),\r\n this.animationTimingMs(),\r\n this.maxDomSize(),\r\n this.enableMouseDrag(),\r\n this.enableTouch(),\r\n this.autoPlay(),\r\n