UNPKG

carousel-angular

Version:

A simple carousel component for Angular 14+ based on 'angular-responsive-carousel' by Ivy Laboratory http://ivylab.space

1,391 lines (1,384 loc) 60.8 kB
import * as i0 from '@angular/core'; import { EventEmitter, Component, Output, Input, HostBinding, HostListener, NgModule } from '@angular/core'; import * as i1 from '@angular/common'; import { CommonModule } from '@angular/common'; class Touches { constructor(properties) { this.eventType = undefined; this.handlers = {}; this.startX = 0; this.startY = 0; this.lastTap = 0; this.doubleTapMinTimeout = 300; this.tapMinTimeout = 200; this.touchstartTime = 0; this.i = 0; this.isMousedown = false; this._touchListeners = { touchstart: 'handleTouchstart', touchmove: 'handleTouchmove', touchend: 'handleTouchend' }; this._mouseListeners = { mousedown: 'handleMousedown', mousemove: 'handleMousemove', mouseup: 'handleMouseup', wheel: 'handleWheel' }; this._otherListeners = { resize: 'handleResize' }; /* * Listeners */ /* Touchstart */ this.handleTouchstart = (event) => { this.elementPosition = this.getElementPosition(); this.touchstartTime = new Date().getTime(); if (this.eventType === undefined) { this.getTouchstartPosition(event); } this.runHandler('touchstart', event); }; /* Touchmove */ this.handleTouchmove = (event) => { const { touches } = event; // Pan if (this.detectPan(touches)) { this.runHandler('pan', event); } // Pinch if (this.detectPinch(event)) { this.runHandler('pinch', event); } // Linear swipe switch (this.detectLinearSwipe(event)) { case 'horizontal-swipe': event.swipeType = 'horizontal-swipe'; this.runHandler('horizontal-swipe', event); break; case 'vertical-swipe': event.swipeType = 'vertical-swipe'; this.runHandler('vertical-swipe', event); break; } // Linear swipe if (this.detectLinearSwipe(event) || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') { this.handleLinearSwipe(event); } }; /* Touchend */ this.handleTouchend = (event) => { const { touches } = event; // Double Tap if (this.detectDoubleTap()) { this.runHandler('double-tap', event); } // Tap this.detectTap(); this.runHandler('touchend', event); this.eventType = 'touchend'; if (touches && touches.length === 0) { this.eventType = undefined; this.i = 0; } }; /* Mousedown */ this.handleMousedown = (event) => { this.isMousedown = true; this.elementPosition = this.getElementPosition(); this.touchstartTime = new Date().getTime(); if (this.eventType === undefined) { this.getMousedownPosition(event); } this.runHandler('mousedown', event); }; /* Mousemove */ this.handleMousemove = (event) => { // event.preventDefault(); if (!this.isMousedown) { return; } // Pan this.runHandler('pan', event); // Linear swipe switch (this.detectLinearSwipe(event)) { case 'horizontal-swipe': event.swipeType = 'horizontal-swipe'; this.runHandler('horizontal-swipe', event); break; case 'vertical-swipe': event.swipeType = 'vertical-swipe'; this.runHandler('vertical-swipe', event); break; } // Linear swipe if (this.detectLinearSwipe(event) || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') { this.handleLinearSwipe(event); } }; /* Mouseup */ this.handleMouseup = (event) => { // Tap this.detectTap(); this.isMousedown = false; this.runHandler('mouseup', event); this.eventType = undefined; this.i = 0; }; /* Wheel */ this.handleWheel = (event) => { this.runHandler('wheel', event); }; /* Resize */ this.handleResize = (event) => { this.runHandler('resize', event); }; this.properties = properties; this.element = this.properties.element; this.elementPosition = this.getElementPosition(); this.toggleEventListeners('addEventListener'); } get touchListeners() { return this.properties.touchListeners ? this.properties.touchListeners : this._touchListeners; } get mouseListeners() { return this.properties.mouseListeners ? this.properties.mouseListeners : this._mouseListeners; } get otherListeners() { return this.properties.otherListeners ? this.properties.otherListeners : this._otherListeners; } destroy() { this.toggleEventListeners('removeEventListener'); } toggleEventListeners(action) { let listeners; if (this.properties.listeners === 'mouse and touch') { listeners = Object.assign(this.touchListeners, this.mouseListeners); } else { listeners = this.detectTouchScreen() ? this.touchListeners : this.mouseListeners; } if (this.properties.resize) { listeners = Object.assign(listeners, this.otherListeners); } for (const listener in listeners) { const handler = listeners[listener]; // Window if (listener === 'resize') { if (action === 'addEventListener') { window.addEventListener(listener, this[handler], false); } if (action === 'removeEventListener') { window.removeEventListener(listener, this[handler], false); } // Document } else if (listener === 'mouseup' || listener === 'mousemove') { if (action === 'addEventListener') { document.addEventListener(listener, this[handler], { passive: false }); } if (action === 'removeEventListener') { document.removeEventListener(listener, this[handler], false); } // Element } else { if (action === 'addEventListener') { this.element.addEventListener(listener, this[handler], false); } if (action === 'removeEventListener') { this.element.removeEventListener(listener, this[handler], false); } } } } addEventListeners(listener) { const handler = this._mouseListeners[listener]; window.addEventListener(listener, this[handler], false); } removeEventListeners(listener) { const handler = this._mouseListeners[listener]; window.removeEventListener(listener, this[handler], false); } handleLinearSwipe(event) { // event.preventDefault(); this.i++; if (this.i > 3) { this.eventType = this.getLinearSwipeType(event); } if (this.eventType === 'horizontal-swipe') { this.runHandler('horizontal-swipe', event); } if (this.eventType === 'vertical-swipe') { this.runHandler('vertical-swipe', event); } } runHandler(eventName, response) { if (this.handlers[eventName]) { this.handlers[eventName](response); } } /* * Detection */ detectPan(touches) { return ((touches.length === 1 && !this.eventType) || this.eventType === 'pan'); } detectDoubleTap() { if (this.eventType != undefined) { return; } const currentTime = new Date().getTime(); const tapLength = currentTime - this.lastTap; clearTimeout(this.doubleTapTimeout); if (tapLength < this.doubleTapMinTimeout && tapLength > 0) { return true; } this.doubleTapTimeout = setTimeout(() => { clearTimeout(this.doubleTapTimeout); }, this.doubleTapMinTimeout); this.lastTap = currentTime; return undefined; } detectTap() { if (this.eventType != undefined) { return; } const currentTime = new Date().getTime(); const tapLength = currentTime - this.touchstartTime; if (tapLength > 0) { if (tapLength < this.tapMinTimeout) { this.runHandler('tap', event); } else { this.runHandler('longtap', event); } } } detectPinch(event) { const { touches } = event; return ((touches.length === 2 && this.eventType === undefined) || this.eventType === 'pinch'); } detectLinearSwipe(event) { const { touches } = event; if (touches) { if ((touches.length === 1 && !this.eventType) || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') { return this.getLinearSwipeType(event); } } else if (!this.eventType || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') { return this.getLinearSwipeType(event); } return undefined; } getLinearSwipeType(event) { if (this.eventType !== 'horizontal-swipe' && this.eventType !== 'vertical-swipe') { const movementX = Math.abs(this.moveLeft(0, event) - this.startX); const movementY = Math.abs(this.moveTop(0, event) - this.startY); if (movementY * 3 > movementX) { return 'vertical-swipe'; } return 'horizontal-swipe'; } return this.eventType; } getElementPosition() { return this.element.getBoundingClientRect(); } getTouchstartPosition(event) { this.startX = event.touches[0].clientX - this.elementPosition.left; this.startY = event.touches[0].clientY - this.elementPosition.top; } getMousedownPosition(event) { this.startX = event.clientX - this.elementPosition.left; this.startY = event.clientY - this.elementPosition.top; } moveLeft(index, event) { const { touches } = event; if (touches) { return touches[index].clientX - this.elementPosition.left; } return event.clientX - this.elementPosition.left; } moveTop(index, event) { const { touches } = event; if (touches) { return touches[index].clientY - this.elementPosition.top; } return event.clientY - this.elementPosition.top; } detectTouchScreen() { const prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); const mq = function (query) { return window.matchMedia(query).matches; }; if ('ontouchstart' in window) { return true; } // include the 'heartz' as a way to have a non matching MQ to help terminate the join // https://git.io/vznFH const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); return mq(query); } /* Public properties and methods */ on(event, handler) { if (event) { this.handlers[event] = handler; } } } class Carousel { constructor(properties, utils, cells, container, slide) { this.properties = properties; this.utils = utils; this.cells = cells; this.container = container; this.slide = slide; this.autoplayId = null; this.handleTouchstart = (event) => { this.container.handleTouchstart(); this.slide.handleTouchstart(event); }; this.handleHorizontalSwipe = (event) => { this.container.handleHorizontalSwipe(); }; this.handleTouchend = (event) => { if (this.properties.freeScroll) { this.container.handleTouchend(); } else { this.container.handleTouchend(true); this.slide.handleTouchend(event); } }; this.isNextArrowDisabled = () => this.slide.isNextArrowDisabled(); this.isPrevArrowDisabled = () => this.slide.isPrevArrowDisabled(); this.init(); } get cellLength() { return this.cells.cellLength; } get lastCellIndex() { return this.cells.cellLength - 1; } get overflowCellsLimit() { return this.utils.overflowCellsLimit; } get autoplayIsPossible() { return this.properties.autoplayIsPossible; } get margin() { return this.properties.margin; } get minSwipeDistance() { return this.properties.minSwipeDistance; } get transitionDuration() { return this.properties.transitionDuration; } get transitionTimingFunction() { return this.properties.transitionTimingFunction; } get fullCellWidth() { return this.properties.cellWidth + this.margin; } get numberOfVisibleCells() { return this.utils.numberOfVisibleCells; } get slideCounter() { return this.slide.counter; } updateProperties(properties) { this.properties = properties; } init() { this.cellsElement = this.properties.cellsElement; this.visibleWidth = this.properties.visibleWidth || this.cellsElement.parentElement.clientWidth; } destroy() { clearInterval(this.autoplayId); } lineUpCells() { this.cells.lineUp(); } handleTransitionend() { this.slide.handleTransitionend(); } next(length = 1) { if (!this.isNextArrowDisabled()) { this.slide.next(length); } } prev(length = 1) { this.slide.prev(length); } autoplay() { if (this.autoplayId === null) { this.autoplayId = setInterval(() => { if (this.autoplayIsPossible) { this.next(); } }, this.properties.autoplayInterval); } } stopAutoplay() { if (this.autoplayId != null) { clearInterval(this.autoplayId); this.autoplayId = null; } } } class Container { constructor(carouselProperties, utils, cells) { this.carouselProperties = carouselProperties; this.utils = utils; this.cells = cells; /* The index of the new position relative to * the active index, for example -1 or +1 */ this.initialPositionX = 0; this.initialElementPositionX = 0; this.pullLimit = 100; this.startTime = 0; this.startX = 0; this.moveX = 0; this.isSwipeInProgress = false; this.init(); } get visibleWidth() { return this.utils.visibleWidth; } get overflowCellsLimit() { return this.utils.overflowCellsLimit; } get element() { return this.carouselProperties.cellsElement; } get freeScroll() { return this.carouselProperties.freeScroll; } get fullCellWidth() { return this.carouselProperties.cellWidth + this.carouselProperties.margin; } get numberOfVisibleCells() { return this.utils.numberOfVisibleCells; } get transitionDuration() { return this.carouselProperties.transitionDuration; } get transitionTimingFunction() { return this.carouselProperties.transitionTimingFunction; } get cellLength() { return this.cells.cellLength; } get tooFewCells() { return this.numberOfVisibleCells > this.cellLength; } get disabled() { return this.tooFewCells; } get margin() { return this.carouselProperties.margin; } updateProperties(carouselProperties) { this.carouselProperties = carouselProperties; } init() { this.setWidth(); } handleTouchstart() { this.startX = this.utils.getStartX(event); this.startTime = new Date().getTime(); this.initialElementPositionX = this.getInitialElementPositionX(); } handleHorizontalSwipe() { if (this.disabled) { return; } if (!this.isSwipeInProgress) { this.startX = this.utils.getStartX(event); this.startTime = new Date().getTime(); this.initialElementPositionX = this.getInitialElementPositionX(); } this.isSwipeInProgress = true; this.moveX = this.utils.getMoveX(event); this.move(); } handleTouchend(simpleProcessing = false) { if (this.disabled) { return; } /* If touchend was passed to the Slide class */ if (simpleProcessing) { this.isSwipeInProgress = false; return; } this.isSwipeInProgress = false; this.finishMoving(); this.clearInitialValues(); } move() { let positionX = this.getMovePositionX(); const isPulled = this.detectPulled(); const direction = this.getDirection(); if (isPulled) { if ((isPulled.edge === 'left' && direction === 'right') || (isPulled.edge === 'right' && direction === 'left')) { positionX = this.slowdownOnPull(positionX); } } this.transformPositionX(positionX, 0); if (this.freeScroll) { this.initialPositionX = positionX; } if (isPulled) { if (isPulled.edge === 'left' && isPulled.overflowX > this.pullLimit) { this.initialPositionX = 0; } if (isPulled.edge === 'right' && isPulled.overflowX > this.pullLimit) { this.initialPositionX = positionX; } } } getMovePositionX() { const distance = this.getDistance(); return this.initialElementPositionX - distance; } getDistance() { return this.startX - this.moveX; } /* If the container is pulled out of the left or right border */ detectPulled() { const currentPositionX = this.getCurrentPositionX(); if (currentPositionX > 0) { return { edge: 'left', positionX: currentPositionX, overflowX: Math.abs(currentPositionX) }; } if (currentPositionX < this.getEndPosition()) { return { edge: 'right', positionX: currentPositionX, overflowX: Math.abs(currentPositionX - this.getEndPosition()) }; } return undefined; } slowdownOnPull(_positionX) { let distance = Math.abs(this.getDistance()); const endPosition = this.getEndPosition(); const isPulled = this.detectPulled(); if (!isPulled) { return 0; } const decelerationRatio = 3 + isPulled.overflowX / 50; let positionX = 0; if (isPulled.edge === 'left') { if (this.initialElementPositionX < 0) { distance -= Math.abs(this.initialElementPositionX); } const rubberPositionX = distance / decelerationRatio; positionX = rubberPositionX; if (this.initialElementPositionX > 0) { positionX = this.initialElementPositionX + rubberPositionX; } if (positionX > this.pullLimit) { positionX = this.pullLimit; } } if (isPulled.edge === 'right') { const rubberPositionX = endPosition + (this.initialElementPositionX - distance - endPosition) / decelerationRatio; const containerWidth = this.getWidth(); positionX = rubberPositionX; if (this.initialElementPositionX < -(containerWidth - this.visibleWidth)) { positionX = containerWidth - this.visibleWidth + this.initialElementPositionX + rubberPositionX; } if (positionX < endPosition - this.pullLimit) { positionX = endPosition - this.pullLimit; } } return positionX; } finishMoving() { const positionX = this.getMovePositionX(); let newPositionX = 0; if (this.freeScroll) { newPositionX = this.getInertia(); } /* Align container while pulling */ newPositionX = this.getAlignedPositionOnPull(newPositionX); this.transformPositionX(newPositionX); this.setInitialPosition(positionX); } /* Returns the new position of the container with inertia */ getInertia() { const distance = this.getDistance(); const currentTime = new Date().getTime(); const tapLength = currentTime - this.startTime; const inertia = (distance / tapLength) * 100; return this.initialPositionX - inertia; } getAlignedPositionOnPull(newPositionX) { const direction = this.getDirection(); if (direction === 'left') { const endPosition = this.getEndPosition(); if (newPositionX < endPosition) { return endPosition; } } return newPositionX; } getCurrentPositionX() { const parentPosition = this.element.parentElement.getBoundingClientRect(); const position = this.element.getBoundingClientRect(); return position.left - parentPosition.left; } getEndPosition() { const width = this.getWidth(); const visibleWidth = this.element.parentElement.clientWidth; return visibleWidth - width; } transformPositionX(value, duration = this.transitionDuration) { if (value === undefined) { return; } this.element.style.transition = `transform ${duration}ms ${this.transitionTimingFunction}`; this.element.style.transform = `translateX(${value}px)`; } getWidth() { return this.cellLength * this.fullCellWidth; } setWidth() { const width = this.getWidth(); this.element.style.width = `${width}px`; } setInitialPosition(position) { this.initialPositionX = position; } getElementPosition() { return this.element.getBoundingClientRect(); } getInitialElementPositionX() { const carouselElementPosition = this.utils.getCarouselElementPosition().left; return this.getElementPosition().left - carouselElementPosition; } clearInitialValues() { this.startX = this.moveX = 0; } getDirection() { const direction = Math.sign(this.startX - this.moveX); if (direction === -1) { return 'right'; } if (direction === 1) { return 'left'; } return undefined; } } class Cells { constructor(carouselProperties, utils) { this.carouselProperties = carouselProperties; this.utils = utils; this.counter = 0; this.init(carouselProperties); } get cellLength() { return this.cells ? this.cells.length : 0; } get fullCellWidth() { return this.carouselProperties.cellWidth + this.carouselProperties.margin; } get cellLengthInLightDOMMode() { return this.cellLength; } get numberOfVisibleCells() { return this.utils.numberOfVisibleCells; } get overflowCellsLimit() { return this.utils.overflowCellsLimit; } updateProperties(carouselProperties) { this.carouselProperties = carouselProperties; } lineUp() { const cells = this.element ? this.element.children : []; for (let i = 0; i < cells.length; i++) { const cell = cells[i]; const positionX = this.getCellPositionInContainer(i); cell.style.transform = `translateX(${positionX}px)`; cell.style.width = `${this.carouselProperties.cellWidth}px`; } } ifSequenceOfCellsIsChanged() { const cells = this.element.children; return cells[0].style.transform !== 'translateX(0px)'; } getCellPositionInContainer(cellIndexInDOMTree) { return cellIndexInDOMTree * this.fullCellWidth; } setCounter(value) { this.counter = value; } init(carouselProperties) { this.element = this.carouselProperties.cellsElement; this.cells = this.element.children; this.visibleWidth = this.carouselProperties.visibleWidth || this.element.parentElement.clientWidth; } } class Slide { constructor(carouselProperties, utils, cells, container) { this.carouselProperties = carouselProperties; this.utils = utils; this.cells = cells; this.container = container; this.slideLength = 0; this.isSlideInProgress = false; this.counter = 0; this._counter = 0; this.distance = 0; this.distanceAbs = 0; this.isNotClickOnArrow = false; this.initialPositionX = 0; this.currentPositionX = 0; /* The slide length has been limited by the limitSlideLength() method */ this.isSlideLengthLimited = false; this.init(); } get fullCellWidth() { return this.carouselProperties.cellWidth + this.carouselProperties.margin; } get margin() { return this.carouselProperties.margin; } get minSwipeDistance() { return this.carouselProperties.minSwipeDistance; } get numberOfVisibleCells() { return this.utils.numberOfVisibleCells; } get visibleCellsOverflowContainer() { return this.utils.visibleCellsOverflowContainer; } /* The position to which the container returns after each slide * in the light DUM tree mode. */ get fixedContainerPosition() { return -(this.overflowCellsLimit * this.fullCellWidth); } get overflowCellsLimit() { return this.utils.overflowCellsLimit; } /* Number of cell elements in the DUM tree */ get cellLength() { return this.cells.cellLength; } updateProperties(carouselProperties) { this.carouselProperties = carouselProperties; this.setVisibleWidth(); } init() { this.visibleWidth = this.carouselProperties.visibleWidth || this.carouselProperties.hostElement.clientWidth; } handleTouchstart() { /* Touchstart event is not called for arrow */ this.isNotClickOnArrow = true; this.isSlideLengthLimited = false; if (!this.isSlideInProgress) { this.initialPositionX = this.container.getCurrentPositionX(); } } handleTouchend() { if (!this.isNotClickOnArrow) { return; } this.currentPositionX = this.container.getCurrentPositionX(); this.distanceAbs = Math.abs(this.initialPositionX - this.currentPositionX); this.distance = this.initialPositionX - this.currentPositionX; this.direction = this.getDirection(); this.isNotClickOnArrow = false; this.handleSlide(); } handleTransitionend() { this.setCounter(); this.isSlideInProgress = false; } handleSlide(customSlideLength = undefined) { const isUsingButton = customSlideLength; let newPositionX; if ((isUsingButton && this.isSlideInProgress) || !this.direction) { return; } /* Custom slide length is used in arrows */ if (customSlideLength) { this.slideLength = this.limitSlideLength(customSlideLength); if (!this.isSlideInProgress) { this.initialPositionX = this.container.getCurrentPositionX(); } } else { this.slideLength = this.getSlideLength(this.distanceAbs); } /* Store intermediate counter value */ this._counter = this.getPreliminaryCounter(); if (this.direction === 'left') { if (!customSlideLength) { this.slideLength = this.limitSlideLength(this.getSlideLength(this.distanceAbs)); } this._counter = this.getPreliminaryCounter(); const isSlidesEnd = this.isSlidesEnd(this._counter); newPositionX = this.getPositionByIndex(this._counter); if (isSlidesEnd) { this._counter = this.counter; newPositionX = this.getPositionByIndex(this.counter); this.slideLength = 0; } } if (this.direction === 'right') { if (!customSlideLength) { this.slideLength = this.getSlideLength(this.distanceAbs); } if (this._counter < 0) { this._counter = this.counter; this.slideLength = this.counter; } newPositionX = this.getPositionByIndex(this.counter - this.slideLength); } if (this.container.getCurrentPositionX() !== newPositionX) { this.isSlideInProgress = true; this.container.transformPositionX(newPositionX); } } next(length = 1) { this.direction = 'left'; this.handleSlide(length); } prev(length = 1) { this.direction = 'right'; this.handleSlide(length); } select(index) { if (index > this.cellLength - 1) { return; } if (index > this.counter) { const length = index - this.counter; this.next(length); } if (index < this.counter) { const length = this.counter - index; this.prev(length); } } getPreliminaryCounter() { if (this.direction === 'left') { return this.counter + this.slideLength; } if (this.direction === 'right') { return this.counter - this.slideLength; } return 0; } /* * Limits the length of the slide during calls to the next() and prev() * methods if the specified position is outside the cell length */ limitSlideLength(slideLength) { if (slideLength > 1) { for (let i = 0; i < slideLength; i++) { const newCounter = this.counter + (slideLength - i); if (!this.isSlidesEnd(newCounter)) { slideLength -= i; this.isSlideLengthLimited = i > 0; break; } } } return slideLength; } /* Offset the container to show the last cell completely */ getPositionCorrection(counter) { let correction = 0; const isLastSlide = this.isLastSlide(counter); if (this.isSlideLengthLimited || isLastSlide) { const cellsWidth = this.cells.cellLengthInLightDOMMode * this.fullCellWidth; if (this.visibleWidth < cellsWidth) { correction = -(this.numberOfVisibleCells * this.fullCellWidth - this.visibleWidth - this.margin); } if (correction >= -this.margin) { correction = 0; } } return correction; } getSlideLength(distanceAbs) { let length = Math.floor(distanceAbs / this.fullCellWidth); if (distanceAbs % this.fullCellWidth >= this.minSwipeDistance) { length++; } return length; } getDistanceAbs() { return Math.abs(this.initialPositionX - this.currentPositionX); } getDirection() { const direction = Math.sign(this.initialPositionX - this.currentPositionX); if (direction === -1) { return 'right'; } if (direction === 1) { return 'left'; } return undefined; } isSlidesEnd(counter) { const margin = this.visibleCellsOverflowContainer ? 1 : 0; const imageLength = this.cells.cellLength; return imageLength - counter + margin < this.numberOfVisibleCells; } isLastSlide(counter) { return this.isSlidesEnd(counter + 1); } setCounter() { if (this.direction === 'left') { this.counter += this.slideLength; } if (this.direction === 'right') { this.counter -= this.slideLength; } } getPositionByIndex(_counter) { let correction = this.getPositionCorrection(this.counter + this.slideLength); let position; if (correction !== 0) { correction += this.fullCellWidth; } if (this.direction === 'right') { correction = 0; } position = -(_counter * this.fullCellWidth - correction); position = this.provideSafePosition(position); return position; } provideSafePosition(position) { const endPosition = this.container.getEndPosition(); if (this.direction === 'left') { if (position > 0) { position = 0; } } if (this.direction === 'right') { if (position < endPosition) { position = endPosition; } } return position; } getPositionWithoutCorrection(value) { const remainder = Math.round(value) % this.fullCellWidth; if (remainder !== 0) { return value - (this.fullCellWidth + remainder); } return value; } isNextArrowDisabled() { return (this.isLastSlide(this.counter) || (!this.visibleCellsOverflowContainer && this.cellLength <= this.numberOfVisibleCells) || (this.visibleCellsOverflowContainer && this.cellLength < this.numberOfVisibleCells)); } isPrevArrowDisabled() { return this.counter === 0; } alignContainerFast() { if (this.ifLeftDOMModeToBeginning(this.counter)) { /* If we have already exited the light DOM mode but * the cells are still out of place */ if (this.cells.ifSequenceOfCellsIsChanged()) { const positionX = -(this.counter * this.fullCellWidth); this.container.transformPositionX(positionX, 0); this.cells.setCounter(this.counter); this.cells.lineUp(); } } } ifLeftDOMModeToBeginning(counter) { let flag; if (counter <= this.overflowCellsLimit) { flag = true; } if (this.counter <= this.overflowCellsLimit) { flag = true; } return flag; } setVisibleWidth() { this.visibleWidth = this.carouselProperties.visibleWidth || this.carouselProperties.hostElement.clientWidth; } } class Utils { constructor(carouselProperties) { this.carouselProperties = carouselProperties; } get margin() { return this.carouselProperties.margin; } get overflowCellsLimit() { return this.carouselProperties.overflowCellsLimit; } get numberOfVisibleCells() { return Math.ceil(this.visibleWidth / this.fullCellWidth); } get visibleCellsOverflowContainer() { return (this.numberOfVisibleCells * this.fullCellWidth - this.margin > this.visibleWidth); } get fullCellWidth() { return this.carouselProperties.cellWidth + this.carouselProperties.margin; } get visibleWidth() { return (this.carouselProperties.visibleWidth || this.carouselProperties.cellsElement.parentElement.clientWidth); } updateProperties(carouselProperties) { this.carouselProperties = carouselProperties; } getStartX(event) { const { touches } = event; const carouselElementPosition = this.getCarouselElementPosition().left; let startX; if (touches) { startX = touches[0].clientX - carouselElementPosition; } else { startX = event.clientX - carouselElementPosition; } return startX; } getMoveX(event) { const { touches } = event; const carouselElementPositionX = this.getCarouselElementPosition().left; if (touches) { return touches[0].clientX - carouselElementPositionX; } return event.clientX - carouselElementPositionX; } getCarouselElementPosition() { return this.carouselProperties.hostElement.getBoundingClientRect(); } } class CarouselComponent { constructor(elementRef, ref) { this.elementRef = elementRef; this.ref = ref; this._isCounter = false; this._cellWidth = 200; this.isMoving = false; this.isNgContent = false; this.events = new EventEmitter(); this.height = 450; this.autoplay = true; this.autoplayInterval = 5000; this.pauseOnHover = true; this.dots = false; this.margin = 10; this.objectFit = 'cover'; this.minSwipeDistance = 10; this.transitionDuration = 200; this.transitionTimingFunction = 'ease-out'; this.counterSeparator = ' / '; this.overflowCellsLimit = 3; this.listeners = 'mouse and touch'; this.cellsToShow = 1; this.cellsToScroll = 1; this.freeScroll = false; this.arrows = true; this.arrowsOutside = false; this.arrowsTheme = 'light'; this.hostClassCarousel = true; this.handleTouchstart = (event) => { this.touches.addEventListeners('mousemove', 'handleMousemove'); this.carousel.handleTouchstart(event); this.isMoving = true; }; this.handleHorizontalSwipe = (event) => { event.preventDefault(); this.carousel.handleHorizontalSwipe(event); }; this.handleTouchend = (event) => { const { touches } = event; this.carousel.handleTouchend(event); this.touches.removeEventListeners('mousemove', 'handleMousemove'); this.isMoving = false; }; this.handleTap = (event) => { const outboundEvent = { name: 'click' }; const nodes = Array.prototype.slice.call(this.cellsElement.children); const cellElement = event.srcElement.closest('.carousel-cell'); const i = nodes.indexOf(cellElement); const cellIndex = nodes.indexOf(cellElement); outboundEvent.cellIndex = cellIndex; }; } get slideCounter() { if (this.carousel) { return this.carousel.slideCounter; } } get lapCounter() { if (this.carousel) { return this.carousel.lapCounter; } } get isLandscape() { return window.innerWidth > window.innerHeight; } get isSafari() { const ua = navigator.userAgent.toLowerCase(); if (ua.indexOf('safari') !== -1) { return !(ua.indexOf('chrome') > -1); } } get counter() { const counter = this.slideCounter; return counter + 1 + this.counterSeparator + this.cellLength; } get cellsElement() { return this.elementRef.nativeElement.querySelector('.carousel-cells'); } get isArrows() { return this.arrows && !this.freeScroll; } get isCounter() { return this._isCounter && this.cellLength > 1; } get activeDotIndex() { return this.slideCounter % this.cellLength; } get cellLimit() { if (this.carousel) { return this.carousel.cellLimit; } } get carouselWidth() { return this.elementRef.nativeElement.clientWidth; } set cellWidth(value) { if (value) { this._cellWidth = value; } } set isCounter(value) { if (value) { this._isCounter = value; } } onWindowResize(event) { if (this.utils.visibleWidth !== this.savedCarouselWidth) { this.resize(); } } onMouseEnter() { if (this.autoplay && this.pauseOnHover) { this.carouselProperties.autoplayIsPossible = false; this.carousel.stopAutoplay(); } } onMouseLeave() { if (this.autoplay && this.pauseOnHover) { this.carouselProperties.autoplayIsPossible = true; this.carousel.autoplay(); } } onDragStart() { return false; } ngOnInit() { this.isNgContent = this.cellsElement.children.length > 0; this.touches = new Touches({ element: this.cellsElement, listeners: this.listeners, mouseListeners: { mousedown: 'handleMousedown', mouseup: 'handleMouseup' } }); this.touches.on('touchstart', this.handleTouchstart); this.touches.on('horizontal-swipe', this.handleHorizontalSwipe); this.touches.on('touchend', this.handleTouchend); this.touches.on('mousedown', this.handleTouchstart); this.touches.on('mouseup', this.handleTouchend); this.touches.on('tap', this.handleTap); this.setDimensions(); } ngAfterViewInit() { this.initCarousel(); this.cellLength = this.getCellLength(); this.dotsArr = Array(this.cellLength).fill(1); this.ref.detectChanges(); this.carousel.lineUpCells(); this.savedCarouselWidth = this.carouselWidth; /* Start detecting changes in the DOM tree */ this.detectDomChanges(); } ngOnChanges(changes) { const isFirstChange = Object.values(changes).some(change => change.isFirstChange()); if (!isFirstChange && (changes.width || changes.height)) { this.setDimensions(); this.initCarousel(); this.carousel.lineUpCells(); this.ref.detectChanges(); } } ngOnDestroy() { this.touches.destroy(); // this.carousel.destroy(); } initCarousel() { this.carouselProperties = { id: this.id, cellsElement: this.elementRef.nativeElement.querySelector('.carousel-cells'), hostElement: this.elementRef.nativeElement, cellWidth: this.getCellWidth(), autoplayInterval: this.autoplayInterval, autoplayIsPossible: true, overflowCellsLimit: this.overflowCellsLimit, visibleWidth: this.width, margin: this.margin, minSwipeDistance: this.minSwipeDistance, transitionDuration: this.transitionDuration, transitionTimingFunction: this.transitionTimingFunction, videoProperties: this.videoProperties, eventHandler: this.events, freeScroll: this.freeScroll }; this.utils = new Utils(this.carouselProperties); this.cells = new Cells(this.carouselProperties, this.utils); this.container = new Container(this.carouselProperties, this.utils, this.cells); this.slide = new Slide(this.carouselProperties, this.utils, this.cells, this.container); if (this.autoplay && this.carousel) { this.carousel.stopAutoplay(); } this.carousel = new Carousel(this.carouselProperties, this.utils, this.cells, this.container, this.slide); if (this.autoplay) { this.carousel.autoplay(); } } resize() { this.landscapeMode = this.isLandscape; this.savedCarouselWidth = this.carouselWidth; this.carouselProperties.cellWidth = this.getCellWidth(); this.cells.updateProperties(this.carouselProperties); this.carousel.updateProperties(this.carouselProperties); this.container.updateProperties(this.carouselProperties); this.slide.updateProperties(this.carouselProperties); this.utils.updateProperties(this.carouselProperties); this.carousel.lineUpCells(); this.slide.select(0); this.ref.detectChanges(); } detectDomChanges() { const observer = new MutationObserver((mutations) => { this.onDomChanges(); }); const config = { attributes: true, childList: true, characterData: true }; observer.observe(this.cellsElement, config); } onDomChanges() { this.cellLength = this.getCellLength(); this.carousel.lineUpCells(); this.ref.detectChanges(); } setDimensions() { this.hostStyleHeight = `${this.height}px`; this.hostStyleWidth = `${this.width}px`; } handleTransitionendCellContainer(event) { if (event.target.className === 'carousel-cells') { this.carousel.handleTransitionend(); } } getCellWidth() { const elementWidth = this.carouselWidth; if (this.cellsToShow) { const margin = this.cellsToShow > 1 ? this.margin : 0; const totalMargin = margin * (this.cellsToShow - 1); return (elementWidth - totalMargin) / this.cellsToShow; } if (this._cellWidth === '100%') { return elementWidth; } return this._cellWidth; } next() { this.carousel.next(this.cellsToScroll); this.carousel.stopAutoplay(); } prev() { this.carousel.prev(this.cellsToScroll); this.carousel.stopAutoplay(); } isNextArrowDisabled() { if (this.carousel) { return this.carousel.isNextArrowDisabled(); } } isPrevArrowDisabled() { if (this.carousel) { return this.carousel.isPrevArrowDisabled(); } } getCellLength() { return this.cellsElement.children.length; } } CarouselComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: CarouselComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); CarouselComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: CarouselComponent, selector: "carousel, [carousel]", inputs: { id: "id", height: "height", width: "width", autoplay: "autoplay", autoplayInterval: "autoplayInterval", pauseOnHover: "pauseOnHover", dots: "dots", borderRadius: "borderRadius", margin: "margin", objectFit: "objectFit", minSwipeDistance: "minSwipeDistance", transitionDuration: "transitionDuration", transitionTimingFunction: "transitionTimingFunction", videoProperties: "videoProperties", counterSeparator: "counterSeparator", overflowCellsLimit: "overflowCellsLimit", listeners: "listeners", cellsToShow: "cellsToShow", cellsToScroll: "cellsToScroll", freeScroll: "freeScroll", arrows: "arrows", arrowsOutside: "arrowsOutside", arrowsTheme: "arrowsTheme", cellWidth: "cellWidth", isCounter: ["counter", "isCounter"] }, outputs: { events: "events" }, host: { listeners: { "window:resize": "onWindowResize($event)", "mouseenter": "onMouseEnter($event)", "mouseleave": "onMouseLeave($event)", "dragstart": "onDragStart($event)" }, properties: { "class.carousel": "this.hostClassCarousel", "style.height": "this.hostStyleHeight", "style.width": "this.h