paragraphs_bundles-1.0.x-dev/modules/paragraph_bundle_3d_carousel/js/paragraph-bundle-3d-carousel.js

modules/paragraph_bundle_3d_carousel/js/paragraph-bundle-3d-carousel.js
/**
 * @file
 * Paragraph Bundle 3D Carousle
 *
 * Filename:     paragraph-bundle-3d-carousel.js
 * Website:      https://www.flashwebcenter.com
 * Developer:    Alaa Haddad https://www.alaahaddad.com.
 */
((Drupal, drupalSettings, once) => {
  'use strict';

  Drupal.behaviors.paragraphBundleCarousel = {
    attach: function(context, settings) {
      let intervalIds = new Map();
      const carousels = once('paragraphBundleCarousel', '.pb__3d-carousel', context);
      if (!carousels.length) {
        return;
      }

      let currentWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
      let autoHelper = [];
      let n = 0;
      let isPaused = false;

      // Utility function to update tabindex based on parent element's attributes
      const updateTabIndexForElements = (parentElement) => {
        const focusableElements = parentElement.querySelectorAll('button, a');
        focusableElements.forEach(el => {
          if (parentElement.getAttribute('aria-hidden') === 'true') {
            el.setAttribute('tabindex', '-1');
          } else if (parentElement.getAttribute('tabindex') === '0') {
            el.setAttribute('tabindex', '0');
          } else {
            el.removeAttribute('tabindex');
          }
        });
      };

      // Centralized Interval Management
      const manageAutoSlideInterval = (carouselId, action, cTime) => {
        let intervalId = intervalIds.get(carouselId);
        if (action === 'clear' && intervalId) {
          clearInterval(intervalId);
          intervalIds.delete(carouselId);
        }
        else if (action === 'start' && cTime > 0 && !isPaused) {
          intervalId = setInterval(() => autoPlay(carouselId), cTime);
          intervalIds.set(carouselId, intervalId);
        }
      };

      const getCarInfo = (carouselId) => {
        const carousel = context.getElementById(carouselId);
        const cells = carousel.querySelectorAll(`#${carouselId}>.pb__caro-item`);
        const cellCount = cells.length;
        const cellWidth = carousel.offsetWidth;
        const theta = 360 / cellCount;
        const radius = Math.round(cellWidth / 2 / Math.tan(Math.PI / cellCount));
        const rawNo = carouselId.split('-')[2];
        return [carousel, cells, cellCount, cellWidth, theta, radius, rawNo];
      };

      const getThisId = th => `pb__3d-carousel-${parseInt(th.slice(5))}`;

      let rotateCarousel = (carouselId, n) => {
        let [carousel, cells, cellCount, , theta, radius] = getCarInfo(carouselId);
        let angle = theta * n * -1;
        carousel.style.transition = 'transform 0.8s ease-in-out';
        carousel.style.transform = `translateZ(${-radius}px) rotateY(${angle}deg)`;

        // Ensure index wraps correctly
        if (n >= cellCount) n = 0;
        if (n < 0) n = cellCount - 1;

        // Update aria-hidden and tabindex for each cell
        cells.forEach((cell, index) => {
          if (index === n) {
            cell.setAttribute('aria-hidden', 'false');
            cell.setAttribute('tabindex', '0');
          } else {
            cell.setAttribute('aria-hidden', 'true');
            cell.setAttribute('tabindex', '-1');
          }

          // Update tabindex for children
          updateTabIndexForElements(cell);
        });

        // Update the aria-label for the current slide number
        const slideNumberElement = context.querySelector(`#index-${carouselId.split('-')[2]}`);
        if (slideNumberElement) {
          slideNumberElement.setAttribute('aria-label', `Slide ${n + 1} of ${cellCount}`);
        }
      };

      let buildCarousel = (carouselId) => {
        let [_, cells, cellCount, , theta, radius, rawNo] = getCarInfo(carouselId);
        let indexNo = context.getElementById('index-' + rawNo);
        let prevBtnId = context.getElementById('prev-' + rawNo);
        let nextBtnId = context.getElementById('next-' + rawNo);
        for (let i = 0; i < cellCount; i++) {
          cells[i].style.opacity = i < cellCount ? 1 : 0;
          cells[i].style.transform = i < cellCount ? `rotateY(${theta * i}deg) translateZ(${radius}px)` : "none";
        }
        cells[0].classList.add('pb__active');
        indexNo.innerHTML = 1;
        nextBtnId.classList.remove('hidden');
        nextBtnId.removeAttribute('tabindex');
        prevBtnId.classList.add('hidden');
        prevBtnId.setAttribute('tabindex', '-1');
        rotateCarousel(carouselId, 0);
      };

      const backward = (carouselId) => {
        const cells = context.querySelectorAll(`#${carouselId}>.pb__caro-item`);
        const cellCount = cells.length;
        const btnId = carouselId.split('-')[2];
        const prevBtnId = context.getElementById(`prev-${btnId}`);
        const nextBtnId = context.getElementById(`next-${btnId}`);
        const indexNo = context.getElementById(`index-${btnId}`);
        for (let i = 0; i < cellCount; i++) {
          if (!cells[i].classList.contains('pb__active')) continue;
          if (i === 0) {
            const prevOn = parseInt(`${btnId}00`);
            const prevOff = parseInt(`${btnId}01`);
            const nextOn = parseInt(`${btnId}11`);
            const nextOff = parseInt(`${btnId}10`);
            if (autoHelper.includes(prevOn)) {
              autoHelper = autoHelper.map((item) => {
                if (item === prevOn) return prevOff;
                if (item === nextOff) return nextOn;
                return item;
              });
            }
            break;
          }
          nextBtnId.classList.remove('hidden');
          nextBtnId.removeAttribute('tabindex');
          cells[i].classList.remove('pb__active');
          i -= 1;
          if (!cells[i]) continue;
          if (i === 0) {
            prevBtnId.classList.add('hidden');
            prevBtnId.setAttribute('tabindex', '-1');
            nextBtnId.classList.remove('hidden');
          }
          cells[i].classList.add('pb__active');
          indexNo.innerHTML = i + 1;
          return i;
        }
      };

      let forward = (carouselId) => {
        let cells = context.querySelectorAll(`#${carouselId}>.pb__caro-item`);
        let cellCount = cells.length;
        let btnId = carouselId.split('-')[2];
        let prevBtnId = context.getElementById(`prev-${btnId}`);
        let nextBtnId = context.getElementById(`next-${btnId}`);
        let indexNo = context.getElementById(`index-${btnId}`);
        for (let i = 0; i < cellCount; i++) {
          if (cells[i].classList.contains('pb__active')) {
            if (i == cellCount - 1) {
              let prevOn = parseInt(`${btnId}00`);
              let prevOff = parseInt(`${btnId}01`);
              let nextOn = parseInt(`${btnId}11`);
              let nextOff = parseInt(`${btnId}10`);
              if (autoHelper.includes(nextOn)) {
                autoHelper = autoHelper.map(item => {
                  if (item == nextOn) {
                    return nextOff;
                  }
                  else if (item == prevOff) {
                    return prevOn;
                  }
                  else {
                    return item;
                  }
                });
              }
              break;
            }
            prevBtnId.classList.remove('hidden');
            prevBtnId.removeAttribute('tabindex');
            cells[i].classList.remove('pb__active');
            i++;
            if (cells[i]) {
              if (i == cellCount - 1) {
                nextBtnId.classList.add('hidden');
                nextBtnId.setAttribute('tabindex', '-1');
                prevBtnId.classList.remove('hidden');
              }
              cells[i].classList.add('pb__active');
              n = i;
              indexNo.innerHTML = n + 1;
              return n;
            }
          }
        }
      };

      let getNumberId = (th) => {
        let idNu = th.split('-')[2];
        parseInt(idNu);
        return idNu;
      };

      let navigateCarousel = (carouselId, directionFunction) => {
        let n = directionFunction(carouselId);
        rotateCarousel(carouselId, n);
      };

      let nextBtn = (carouselId) => {
        let cTimeId = carouselId.split('-')[2];
        let cTime = parseInt(context.querySelector(`#co-${cTimeId} > .pb__ps-value`).textContent);
        navigateCarousel(carouselId, forward);
        manageAutoSlideInterval(carouselId, 'clear');
        if (!isPaused) {
          manageAutoSlideInterval(carouselId, 'start', cTime);
        }
      };

      let prevBtn = (carouselId) => {
        let cTimeId = carouselId.split('-')[2];
        let cTime = parseInt(context.querySelector(`#co-${cTimeId} > .pb__ps-value`).textContent);
        navigateCarousel(carouselId, backward);
        manageAutoSlideInterval(carouselId, 'clear');
        if (!isPaused) {
          manageAutoSlideInterval(carouselId, 'start', cTime);
        }
      };

      let autoPlay = (carouselId) => {
        let d = getNumberId(carouselId);
        let prevOn = parseInt(d + 0 + 0);
        let nextOn = parseInt(d + 1 + 1);
        if ((autoHelper.indexOf(nextOn) >= 0)) {
          n = forward(carouselId);
          rotateCarousel(carouselId, n);
        }
        if ((autoHelper.indexOf(prevOn) >= 0)) {
          n = backward(carouselId);
          rotateCarousel(carouselId, n);
        }
      };

      let initCarousel = (carousels) => {
        autoHelper = [];
        for (let j = 0; j < carousels.length; j++) {
          let carouselC = carousels[j];
          let carouselId = carouselC.id;
          let shortId = carouselId.split('-')[2];
          autoHelper.push(parseInt(shortId + 1 + 1));
          autoHelper.push(parseInt(shortId + 0 + 1));
          buildCarousel(carouselId);
        }
      };

      let setAutoPlayInterval = (carouselId, cTime) => {
        manageAutoSlideInterval(carouselId, 'clear');
        if (cTime > 0) {
          let intervalId = setInterval(() => {
            autoPlay(carouselId);
          }, cTime);
          intervalIds.set(carouselId, intervalId);
          return intervalId;
        }
      };

      carousels.forEach(carouselC => {
        let carouselId = carouselC.id;
        let cTimeId = carouselId.split('-')[2];
        let cTime = parseInt(context.querySelector(`#co-${cTimeId} > .pb__ps-value`).textContent);

        cTime = parseInt(cTime);
        if (cTime != 0) {
          let intervalId = setAutoPlayInterval(carouselId, cTime); // Create and store interval

          let stopOnHover = context.getElementById(carouselId);
          let playPause = context.getElementById(`btn-${cTimeId}`);
          playPause.style.display = 'inline';

          stopOnHover.addEventListener('mouseover', function() {
            if (playPause.classList.contains('pb__play')) {
              clearInterval(intervalIds.get(carouselId)); // Use interval from the Map
            }
          });

          stopOnHover.addEventListener('mouseout', function() {
            if (playPause.classList.contains('pb__play')) {
              intervalIds.set(carouselId, setAutoPlayInterval(carouselId, cTime)); // Reset the interval
            }
          });

          playPause.addEventListener('click', function() {
            if (this.classList.contains('pb__play')) {
              this.classList.replace('pb__play', 'pb__pause');
              this.innerHTML = '<span>&#9654;</span>';
              isPaused = true;
              manageAutoSlideInterval(carouselId, 'clear');
            }
            else {
              this.classList.replace('pb__pause', 'pb__play');
              this.innerHTML = '<span>&#10073;&nbsp;&#10073;</span>';
              isPaused = false;
              manageAutoSlideInterval(carouselId, 'start', cTime);
            }
          });

        }

        // Add keyboard navigation support
        carouselC.addEventListener('keydown', (event) => {
          if (event.key === 'ArrowRight') {
            nextBtn(carouselId);
          } else if (event.key === 'ArrowLeft') {
            prevBtn(carouselId);
          }
        });

      });

      const handleWindowResize = () => {
        n = 0;
        let newWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        if (Math.abs(newWidth - currentWidth) >= 50) {
          initCarousel(carousels);
          currentWidth = newWidth;
        }
      };

      once('init-pb__next', '.pb__caro-options .pb__next', context)
        .forEach(element => {
          element.addEventListener('click', function(event) {
            nextBtn(getThisId(event.currentTarget.id));
          });
        });

      once('init-pb__prev', '.pb__caro-options .pb__prev', context)
        .forEach(element => {
          element.addEventListener('click', function(event) {
            prevBtn(getThisId(event.currentTarget.id));
          });
        });

      window.addEventListener('resize', handleWindowResize);
      initCarousel(carousels);

    }
  };
})(Drupal, drupalSettings, once);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc