diff --git a/blocks/imageslider/imageslider.css b/blocks/imageslider/imageslider.css new file mode 100644 index 00000000..ce55ebc2 --- /dev/null +++ b/blocks/imageslider/imageslider.css @@ -0,0 +1,593 @@ +/* + Default imageslider Styling. + Same as .products on the landing page. + Variant stylings can be found below. +*/ + +/* stylelint-disable no-descending-specificity */ + +main div.imageslider-wrapper { + /* width: 100%; */ + position: relative; + max-width: unset; + } + + main div.imageslider-wrapper:last-child { + margin-bottom: 0 !important; + } + + main .imageslider { + box-sizing: border-box; + display: flex; + overflow: hidden; + margin: auto; + width: 90%; /* default - same as .products */ + padding : 10px 315px; + } + + main .imageslider-item { + position:relative; + box-sizing: border-box; + width: 250px; + margin-right: 10px; + flex: 1 0 auto; + display: flex; + align-items: center; + justify-content: center; + padding: 0; /* default - same as .products */ + } + + main .imageslider-item-columns-container { + width: 100%; + align-items: center; + justify-content: center; + } + + main .imageslider-item-image img { + display: block; + width: 100%; /* default - same as .products */ + } + + main .imageslider.full-bg .imageslider-item { + position: relative; + border-width: 1px; + } + + main .imageslider-item-text h3 { + font-size: 32px; + font-weight: lighter; + } + + main .imageslider-item-text .button-container { + display: inline-block; + margin-top: 0; + } + + main .imageslider.full-bg .imageslider-item-text .button-container { + width: 100%; + max-width: 80%; + } + + main .imageslider.full-bg .primary { + width: 100%; + } + + main .imageslider-item-text .button-container .secondary { + margin-left: 15px; + } + + main .imageslider-nav-button { + z-index: 99; + min-width: unset; + padding: 4px; + border-radius: 50%; + position: absolute; + top: 88%; + margin:0 -15px; + transform: translate(0, -50%); + display: flex; + justify-content: center; + align-items: center; + color: #fff; /* default - same as .products */ + border: 1.5px solid #adb3b7; /* default - same as .products */ + background-color: #adb3b7; /* default - same as .products */ + font-weight: bolder; + height: 20px; + } + + main .imageslider-nav-button:hover { + z-index: 99; + min-width: unset; + padding: 4px; + border-radius: 50%; + position: absolute; + top: 88%; + margin:0 -15px; + transform: translate(0, -50%); + display: flex; + justify-content: center; + align-items: center; + color: #fff; /* default - same as .products */ + border: 1.5px solid #a3a3a3; /* default - same as .products */ + background-color: #a3a3a3; /* default - same as .products */ + font-weight: bolder; + height: 20px; + } + + main .imageslider-nav-button.disabled { + display: none; + } + + main .imageslider-nav-button .icon { + height: 38px; + width: 38px; + stroke-width: 20; + } + + main .imageslider-nav-left { + left: 20px; + } + + main .imageslider-nav-right { + right: 20px; + } + + main .imageslider-dot-buttons { + display: flex; + justify-content: center; + gap: 10px; + min-width: unset; + margin-top: 20px; + } + + main .imageslider-dot-button { + min-width: unset; + height: 20px; + width: 20px; + background-color: transparent; + border: 1px solid #cacaca; + border-radius: 50%; + padding: 0; + } + + main .imageslider-dot-button.selected { + background-color: #cacaca; + border: 0; + } + + @media only screen and (max-width: 1023px) { + main .imageslider { + padding-left: 33%; + padding-right: 0; + } + + main .imageslider-nav-button { + margin:0 -5px; + } + + main .imageslider-nav-button:hover { + margin:0 -5px; + } + } + + @media only screen and (min-width: 992px) { + main .imageslider.full-bg .primary { + background-color: transparent; + border: 1px solid #fff; + } + + main .imageslider.full-bg .primary:focus { + color: #fff; + } + + main .imageslider.full-bg .primary:hover { + color: var(--link-hover-color); + background-color: #fff; + } + + main .imageslider.full-bg .primary:hover .button-border{ + display: none; + } + + main .imageslider.full-bg .imageslider-item-column:first-child { + position: absolute; + width: 100%; + left: 0; + top: 0; + padding: 0; + } + + main .imageslider.full-bg .imageslider-item-column:last-child { + color: #fff; + position: relative; + display: flex; + align-items: center; + } + + main .imageslider.full-bg .imageslider-item-column:first-child , + main .imageslider.full-bg .imageslider-item-image, + main .imageslider.full-bg .imageslider-item-image .picture, + main .imageslider.full-bg .imageslider-item-image img { + height: 100%; + } + + main .imageslider.full-bg .imageslider-item-columns-container { + justify-content: flex-end; + min-height: 500px; + } + + main .imageslider.full-bg .imageslider-item-image img { + object-fit: cover; + max-height: 100%; + } + + main .imageslider.full-bg .imageslider-item-column:first-child .picture:last-child { + display: none; + } + } + + @media only screen and (max-width: 991px) { + main .imageslider-item-columns-container { + flex-direction: column; + gap: 30px; + } + + main .imageslider-item-column { + width: 100%; + padding: 0; + align-items: center; + } + + main .imageslider.full-bg .imageslider-item-column:first-child .picture:first-child { + display: none; + } + } + + @media only screen and (max-width: 767px) { + main .imageslider { + padding-left: 33%; + padding-right: 0; + } + + main .imageslider-item { + padding: 0; + } + + main .imageslider-item-text h3 { + font-size: 24px; + } + + main .imageslider-item-text .button-container { + margin-top: 20px; + margin-bottom: 0; + max-width: 100%; + width: 100%; + } + + main .imageslider-item-text .button-container .button { + max-width: 100%; + width: 100%; + font-size: 12px; + } + + main .imageslider-nav-button .icon { + height: 26px; + width: 26px; + } + + main .imageslider-nav-button { + padding: 2px; + height: 15px; + margin:0 -10px; + } + + main .imageslider-nav-button:hover { + padding: 2px; + height: 15px; + margin:0 -10px; + } + } + + @media only screen and (max-width: 576px) { + main .imageslider-item-text .button-container { + margin-bottom: 0; + } + + main .imageslider-item-text .button-container .button { + margin-left: 0; + } + } + + @media only screen and (max-width: 420px) { + main .imageslider-item-text .button-container { + max-width: unset !important; + display: block; + } + + main .imageslider-item-text .button-container .button { + max-width: unset !important; + } + + main .imageslider a.button { + display: block; + } + } + + /* WAVE imageslider variant */ + main div.imageslider-wrapper.wave { + width: 1170px; + } + + main .imageslider.wave { + padding: 0; + } + + main .imageslider.wave .imageslider-item-columns-container { + gap: 2em; + } + + main .imageslider.wave .imageslider-item { + padding: 20px 15px; + height: 100%; + } + + main .imageslider.wave .imageslider-item-column { + width: unset; + padding: 0; + } + + main .imageslider.wave .imageslider-item-image img { + height: 353px; + width: 330px; + } + + main .imageslider.wave .imageslider-item-text { + margin-top: 13%; + max-width: 597px; + padding-right: 15px; + text-align: center; + position: relative; + } + + main .imageslider.wave .imageslider-item-text p { + color: var(--text-white); + font-size: 27px; + } + + main .imageslider.wave .imageslider-item-text .button-container a.secondary, + main .imageslider.wave .imageslider-item-text .button-container a.secondary:hover { + background-color: transparent; + color: var(--text-white); + border: 1.5px solid var(--text-white); + } + + main .imageslider-wrapper.wave .imageslider-dot-buttons { + margin-top: 0; + } + + main .imageslider-wrapper.wave .imageslider-dot-button { + background-color: var(--background-color); + } + + main .imageslider-wrapper.wave .imageslider-nav-button:hover { + color: #adb3b7; + border: 1.5px solid #adb3b7; + background-color: inherit; + } + + @media only screen and (max-width: 1200px) and (min-width: 992px) { + main div.imageslider-wrapper.wave { + width: 970px; + } + + main .imageslider.wave { + max-width: 90%; + height: auto; + margin: auto; + } + + main .imageslider.wave .imageslider-item-columns-container { + gap: 1em; + } + + main .imageslider.wave .imageslider-item-text { + max-width: 530px; + } + + main .imageslider.wave .imageslider-item-image img { + height: 310px; + width: 200px; + } + } + + @media only screen and (max-width: 992px) { + main div.imageslider-wrapper.wave { + width: 750px; + } + + main .imageslider.wave .imageslider-item-image img { + display: none; + } + } + + @media only screen and (max-width: 767px) { + main div.imageslider-wrapper.wave { + width: 100%; + } + + main .imageslider.wave { + /* + Ugly hack because you cannot mix overflow-x: hidden with overflow-y: visible + https://stackoverflow.com/a/39554003 + */ + padding-top: 170px; + margin-top: -170px; + } + + main .imageslider.wave .imageslider-item { + position: relative; + padding: 0 30px; + max-width: 90%; + } + + main .imageslider.wave .imageslider-item-columns-container { + gap: 0; + } + + main .imageslider.wave .imageslider-item-image img { + max-width: unset; + display: block; + height: 290px; + width: 260px; + flex-shrink: 0; + } + + main .imageslider.wave .imageslider-item-image { + position: absolute; + left: 50%; + transform: translate3d(0 0 0); + transition-duration: 0ms; + opacity: 1; + margin: auto; + display: block; + top: -180px; + } + + main .imageslider.wave .imageslider-item-text { + margin-top: 140px; + padding-right: 0; + padding-left: 0; + padding-bottom: 25px; + max-width: unset; + } + + main .imageslider-wrapper.wave .imageslider-dot-buttons { + margin-bottom: 25px; + } + } + + /* WAVE Blue Variant */ + main div.imageslider-wrapper.wave.blue .imageslider-dot-button.selected { + background-color: #23bfd9; + } + + /* WAVE BlueGreen Variant */ + main div.imageslider-wrapper.wave.bluegreen .imageslider-dot-button.selected { + background-color: #86c440; + } + + main .imageslider .imageslider-item-text blockquote { + margin: 0; + position: static; + } + + main .imageslider .imageslider-item .imageslider-item-text blockquote ~ p, + main .imageslider .imageslider-item .imageslider-item-text blockquote > p { + padding: 0; + font-style: normal; + font-size: 23px; + line-height: 28px; + } + + main .imageslider .imageslider-item .imageslider-item-text blockquote > p::before, + main .imageslider .imageslider-item .imageslider-item-text blockquote > p::after { + content: none; + } + + main .imageslider blockquote::before, + main .imageslider blockquote::after { + content: ""; + position: absolute; + width: 100px; + height: 100px; + background-position: center; + background-size: 100px; + background-repeat: no-repeat; + } + + main .imageslider blockquote::before { + background-image: url('/images/quote-left.png'); + left: -50px; + top: -89px; + } + + main .imageslider blockquote::after { + background-image: url('/images/quote-right.png'); + right: -1.5em; + bottom: -60px; + } + + @media only screen and (max-width: 992px) { + main .imageslider blockquote::before, + main .imageslider blockquote::after { + width: 33px; + height: 33px; + background-size: 33px; + } + + main .imageslider blockquote::before { + left: -8px; + top: -35px; + } + + main .imageslider blockquote::after { + right: 0; + bottom: 0; + } + } + + @media only screen and (max-width: 767px) { + main .imageslider .imageslider-item .imageslider-item-text blockquote > p { + font-size: 16px; + } + + main .imageslider .imageslider-item .imageslider-item-text blockquote + p { + font-size: 18px; + } + } + + main .block.imageslider.full-size .imageslider-item-column { + width: auto; + } + + main .block.imageslider.full-size .imageslider-item-image img { + max-height: none; + } + + .imageslider-main { + box-sizing: border-box; + display: flex; + overflow: hidden; + margin: auto; + width: 90%; + padding: 20px 0; + } + + .imageslider-main .imageslider-item { + position:relative; + box-sizing: border-box; + width: 100%; + margin-right: 0; + flex: 1 0 auto; + display: flex; + align-items: center; + justify-content: center; + padding: 0; /* default - same as .products */ + } + + .imageslider-main .imageslider-item-columns-container { + width: 100%; + align-items: center; + justify-content: center; + } + + .imageslider-main .imageslider-item-column { + width: 100%; + } + + .imageslider-main .imageslider-item-image img { + display: block; + object-fit: contain; + width: 100%; /* default - same as .products */ + height: auto;/* default - same as .products */ + } \ No newline at end of file diff --git a/blocks/imageslider/imageslider.js b/blocks/imageslider/imageslider.js new file mode 100644 index 00000000..316b9dad --- /dev/null +++ b/blocks/imageslider/imageslider.js @@ -0,0 +1,631 @@ +/* eslint-disable no-unused-expressions */ +import { decorateIcons, loadCSS } from '../../scripts/aem.js'; +import { div, p, span } from '../../scripts/dom-builder.js'; + +const AUTOSCROLL_INTERVAL = 7000; + +/** + * Clone a imageslider item + * @param {Element} item imageslider item to be cloned + * @returns the clone of the imageslider item + */ +function createClone(item) { + const clone = item.cloneNode(true); + clone.classList.add('clone'); + clone.classList.remove('selected'); + + return clone; +} + +class ImageSlider { + constructor(block, data, config) { + // Set defaults + this.cssFiles = []; + this.defaultStyling = false; + this.dotButtons = false; + this.navButtons = true; + this.counter = false; + this.infiniteScroll = true; + this.autoScroll = false; // only available with infinite scroll + this.autoScrollInterval = AUTOSCROLL_INTERVAL; + this.currentIndex = 0; + this.counterText = ''; + this.counterNavButtons = true; + this.cardRenderer = this; + // this is primarily controlled by CSS, + // but we need to know then intention for scrolling pourposes + this.visibleItems = [ + { + items: 1, + condition: () => true, + }, + ]; + + // Set information + this.block = block; + this.data = data || [...block.children]; + + // Will be replaced after rendering, if available + this.navButtonLeft = null; + this.navButtonRight = null; + + // Apply overwrites + Object.assign(this, config); + + if (this.getCurrentVisibleItems() >= this.data.length) { + this.infiniteScroll = false; + this.navButtons = false; + this.block.classList.add('fully-visible'); + } + + if (this.defaultStyling) { + this.cssFiles.push('/blocks/imageslider/imageslider.css'); + } + } + + getBlockPadding() { + if (!this.blockStyle) { + this.blockStyle = window.getComputedStyle(this.block); + } + return +(this.blockStyle.getPropertyValue('padding-left').replace('px', '')); + } + + getMainBlockPadding() { + if (!this.blockStyleMain) { + this.blockStyleMain = window.getComputedStyle(this.block.parentElement.firstChild); + } + return +(this.blockStyleMain.getPropertyValue('padding-left').replace('px', '')); + } + + updateCounterText(newIndex = this.currentIndex) { + this.currentIndex = newIndex; + if (this.counter) { + const items = this.block.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + const counterTextBlock = this.block.parentElement.querySelector('.imageslider-counter .imageslider-counter-text p'); + const pageCounter = `${this.currentIndex + 1} / ${items.length}`; + counterTextBlock.innerHTML = this.counterText ? `${this.counterText} ${pageCounter}` : pageCounter; + } + } + + /** + * Scroll the imageslider to the next item + */ + nextItem() { + !this.infiniteScroll && this.navButtonRight && this.navButtonRight.classList.remove('disabled'); + !this.infiniteScroll && this.navButtonLeft && this.navButtonLeft.classList.remove('disabled'); + + const dotButtons = this.block.parentNode.querySelectorAll('.imageslider-dot-button'); + const items = this.block.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + const selectedItem = this.block.querySelector('.imageslider-item.selected'); + const selectedItemMain = this.block.parentElement.firstChild.querySelector('.imageslider-item.selected'); + + const itemsMain = this.block.parentElement.firstChild.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + + let index = [...items].indexOf(selectedItem); + index = index !== -1 ? index : 0; + + const newIndex = (index + 1) % items.length; + const newSelectedItem = items[newIndex]; + const newSelectedItemMain = itemsMain[newIndex]; + if (newIndex === 0 && !this.infiniteScroll) { + return; + } + + if (newIndex === items.length - this.getCurrentVisibleItems() && !this.infiniteScroll) { + this.navButtonRight.classList.add('disabled'); + } + + if (newIndex === 0) { + // create the ilusion of infinite scrolling + newSelectedItem.parentNode.scrollTo({ + top: 0, + left: ( + newSelectedItem.previousElementSibling.offsetLeft + - this.getBlockPadding() + - this.block.offsetLeft + ), + + }); + + newSelectedItemMain.parentNode.scrollTo({ + top: 0, + left: ( + newSelectedItemMain.previousElementSibling.offsetLeft + - this.getMainBlockPadding() + - this.block.offsetLeft + + ), + + }); + } + + if (newIndex === 2) { + selectedItemMain.previousElementSibling && selectedItemMain.previousElementSibling.removeAttribute('style'); + } + + newSelectedItem.parentNode.scrollTo({ + top: 0, + left: newSelectedItem.offsetLeft - this.getBlockPadding() - this.block.offsetLeft, + behavior: 'smooth', + }); + + newSelectedItemMain.parentNode.scrollTo({ + top: 0, + left: newSelectedItemMain.offsetLeft + - this.getMainBlockPadding() + - this.block.offsetLeft, + behavior: 'smooth', + }); + + items.forEach((item) => item.classList.remove('selected')); + itemsMain.forEach((item) => item.classList.remove('selected')); + dotButtons.forEach((item) => item.classList.remove('selected')); + newSelectedItem.classList.add('selected'); + newSelectedItemMain.classList.add('selected'); + if (dotButtons && dotButtons.length !== 0) { + dotButtons[newIndex].classList.add('selected'); + } + + this.updateCounterText(newIndex); + } + + getCurrentVisibleItems() { + return this.visibleItems + .filter((e) => !e.condition || e.condition())[0].items; + } + + /** + * Scroll the imageslider to the previous item + */ + prevItem() { + !this.infiniteScroll && this.navButtonRight && this.navButtonRight.classList.remove('disabled'); + !this.infiniteScroll && this.navButtonLeft && this.navButtonLeft.classList.remove('disabled'); + + const dotButtons = this.block.parentNode.querySelectorAll('.imageslider-dot-button'); + const items = this.block.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + const selectedItem = this.block.querySelector('.imageslider-item.selected'); + const selectedItemMain = this.block.parentElement.firstChild.querySelector('.imageslider-item.selected'); + selectedItemMain.previousElementSibling && selectedItemMain.previousElementSibling.removeAttribute('style'); + + const itemsMain = this.block.parentElement.firstChild.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + + let index = [...items].indexOf(selectedItem); + index = index !== -1 ? index : 0; + const newIndex = index - 1 < 0 ? items.length - 1 : index - 1; + const newSelectedItem = items[newIndex]; + const newSelectedItemMain = itemsMain[newIndex]; + + if (newIndex === items.length - 1 && !this.infiniteScroll) { + return; + } + + if (newIndex === 0 && !this.infiniteScroll) { + this.navButtonLeft.classList.add('disabled'); + } + + if (newIndex === items.length - 1) { + // create the ilusion of infinite scrolling + newSelectedItem.parentNode.scrollTo({ + top: 0, + left: ( + newSelectedItem.nextElementSibling.offsetLeft + - this.getBlockPadding() + - this.block.offsetLeft + ), + + }); + + newSelectedItemMain.parentNode.scrollTo({ + top: 0, + left: ( + newSelectedItemMain.nextElementSibling.offsetLeft + - this.getMainBlockPadding() + - this.block.offsetLeft + ), + + }); + } + + newSelectedItem.parentNode.scrollTo({ + top: 0, + left: newSelectedItem.offsetLeft - this.getBlockPadding() - this.block.offsetLeft, + behavior: 'smooth', + }); + + newSelectedItemMain.parentNode.scrollTo({ + top: 0, + left: newSelectedItemMain.offsetLeft - this.getMainBlockPadding() - this.block.offsetLeft, + behavior: 'smooth', + }); + + items.forEach((item) => item.classList.remove('selected')); + itemsMain.forEach((item) => item.classList.remove('selected')); + dotButtons.forEach((item) => item.classList.remove('selected')); + newSelectedItem.classList.add('selected'); + newSelectedItemMain.classList.add('selected'); + if (dotButtons && dotButtons.length !== 0) { + dotButtons[newIndex].classList.add('selected'); + } + + this.updateCounterText(newIndex); + } + + /** + * Create clone items at the beginning and end of the imageslider + * to give the appearance of infinite scrolling + */ + createClones() { + if (this.block.children.length < 3) return; + + const initialChildren = [...this.block.children]; + + this.block.parentElement.firstChild.lastChild.after(createClone(initialChildren[0])); + // this.block.parentElement.firstChild.lastChild.after(createClone(initialChildren[1])); + // this.block.parentElement.firstChild.lastChild.after(createClone(initialChildren[2])); + + this.block.parentElement.firstChild.firstChild + .before(createClone(initialChildren[initialChildren.length - 1])); + + this.block.lastChild.after(createClone(initialChildren[0])); + this.block.lastChild.after(createClone(initialChildren[1])); + this.block.lastChild.after(createClone(initialChildren[2])); + + this.block.firstChild.before(createClone(initialChildren[initialChildren.length - 1])); + this.block.firstChild.before(createClone(initialChildren[initialChildren.length - 2])); + this.block.firstChild.before(createClone(initialChildren[initialChildren.length - 3])); + } + + /** + * Create left and right arrow navigation buttons + */ + createNavButtons(parentElement) { + const buttonLeft = document.createElement('button'); + buttonLeft.classList.add('imageslider-nav-left'); + buttonLeft.classList.add('imageslider-nav-button'); + buttonLeft.ariaLabel = 'Scroll to previous item'; + buttonLeft.innerText = '<'; + // buttonLeft.append(span({ class: 'icon icon-chevron-left' })); + buttonLeft.addEventListener('click', () => { + clearInterval(this.intervalId); + this.prevItem(); + }); + + if (!this.infiniteScroll) { + buttonLeft.classList.add('disabled'); + } + + const buttonRight = document.createElement('button'); + buttonRight.classList.add('imageslider-nav-right'); + buttonRight.classList.add('imageslider-nav-button'); + buttonRight.ariaLabel = 'Scroll to next item'; + buttonRight.innerText = '>'; + // buttonRight.append(span({ class: 'icon icon-chevron-right' })); + buttonRight.addEventListener('click', () => { + clearInterval(this.intervalId); + this.nextItem(); + }); + + [buttonLeft, buttonRight].forEach((navButton) => { + navButton.classList.add('imageslider-nav-button'); + parentElement.append(navButton); + }); + + decorateIcons(buttonLeft); + decorateIcons(buttonRight); + this.navButtonLeft = buttonLeft; + this.navButtonRight = buttonRight; + } + + /** + * Adds event listeners for touch UI swiping + */ + addSwipeCapability() { + if (this.block.swipeCapabilityAdded) { + return; + } + + let touchstartX = 0; + let touchendX = 0; + + this.block.addEventListener('touchstart', (e) => { + touchstartX = e.changedTouches[0].screenX; + }, { passive: true }); + + this.block.addEventListener('touchend', (e) => { + touchendX = e.changedTouches[0].screenX; + if (Math.abs(touchendX - touchstartX) < 10) { + return; + } + + if (touchendX < touchstartX) { + clearInterval(this.intervalId); + this.nextItem(); + } + + if (touchendX > touchstartX) { + clearInterval(this.intervalId); + this.prevItem(); + } + }, { passive: true }); + this.block.swipeCapabilityAdded = true; + } + + setInitialScrollingPosition() { + const scrollToSelectedItem = () => { + const item = this.block.querySelector('.imageslider-item.selected'); + item.parentNode.scrollTo({ + top: 0, + left: item.offsetLeft - this.getBlockPadding() - this.block.offsetLeft, + }); + }; + + const section = this.block.closest('.section'); + + const observer = new MutationObserver((mutationList) => { + mutationList.forEach((mutation) => { + if (mutation.type === 'attributes' + && mutation.attributeName === 'data-section-status' + && section.attributes.getNamedItem('data-section-status').value === 'loaded') { + scrollToSelectedItem(); + observer.disconnect(); + } + }); + }); + + observer.observe(section, { attributes: true }); + + // just in case the mutation observer didn't work + setTimeout(scrollToSelectedItem, 700); + + // ensure that we disconnect the observer + // if the animation has kicked in, we for sure no longer need it + setTimeout(() => { observer.disconnect(); }, AUTOSCROLL_INTERVAL); + } + + createDotButtons() { + const buttons = document.createElement('div'); + buttons.className = 'imageslider-dot-buttons'; + const items = [...this.block.children]; + + items.forEach((item, i) => { + const button = document.createElement('button'); + button.ariaLabel = `Scroll to item ${i + 1}`; + button.classList.add('imageslider-dot-button'); + if (i === this.currentIndex) { + button.classList.add('selected'); + } + + button.addEventListener('click', () => { + clearInterval(this.intervalId); + this.block.scrollTo({ + top: 0, + left: item.offsetLeft - this.getBlockPadding(), + behavior: 'smooth', + }); + [...buttons.children].forEach((r) => r.classList.remove('selected')); + items.forEach((r) => r.classList.remove('selected')); + button.classList.add('selected'); + item.classList.add('selected'); + this.updateCounterText(i); + }); + buttons.append(button); + }); + this.block.parentElement.append(buttons); + } + + clickthumbnails() { + const items = [...this.block.children]; + items.forEach((item) => { + // console.log('item', item); + // console.log('i', i); + // console.log('count : ',count); + + item.addEventListener('click', () => { + // console.log('currIndx', this.currentIndex); + // console.log('i :: ', i); + // this.nextItem(); + + const selectedItem = this.block.querySelector('.imageslider-item.selected'); + if (item.classList.toString() + === selectedItem.previousElementSibling.classList.toString()) { + this.prevItem(); + } else if (item.classList.toString() + === selectedItem.previousElementSibling.previousElementSibling.classList.toString()) { + this.prevItem(); + this.prevItem(); + } else if (item.classList.toString() + === selectedItem.nextElementSibling.classList.toString()) { + this.nextItem(); + } else if (item.classList.toString() + === selectedItem.nextElementSibling.nextElementSibling.classList.toString()) { + this.nextItem(); + this.nextItem(); + } + }); + }); + } + + setSliderWidth() { + const deviceWidth = window.innerWidth; + if (deviceWidth < 1024) { + const imgWidth = Math.ceil((deviceWidth - 80) / 4); + this.block.querySelectorAll('.imageslider-item').forEach((item) => { + item.setAttribute('style', `width:${imgWidth}px;`); + }); + } + } + + setInitialImage() { + const selectedItemMain = this.block.parentElement.firstChild.querySelector('.imageslider-item.selected'); + selectedItemMain.previousElementSibling && selectedItemMain.previousElementSibling.setAttribute('style', 'display:none'); + } + + createCounter() { + const counter = div( + { class: 'imageslider-counter' }, + div( + { class: 'imageslider-counter-text' }, + p(''), + ), + ); + if (this.counterNavButtons) { + this.createNavButtons(counter); + } + this.block.parentElement.append(counter); + this.updateCounterText(); + } + + /* + * Changing the default rendering may break imagesliders that rely on it + * (e.g. CSS might not match anymore) + */ + // eslint-disable-next-line class-methods-use-this + renderItem(item) { + // create the imageslider content + const columnContainer = document.createElement('div'); + columnContainer.classList.add('imageslider-item-columns-container'); + + const columns = [document.createElement('div'), document.createElement('div')]; + + const itemChildren = [...item.children]; + itemChildren.forEach((itemChild, idx) => { + if (itemChild.querySelector('img')) { + itemChild.classList.add('imageslider-item-image'); + } else { + itemChild.classList.add('imageslider-item-text'); + } + columns[idx].appendChild(itemChild); + }); + + columns.forEach((column) => { + column.classList.add('imageslider-item-column'); + columnContainer.appendChild(column); + }); + return columnContainer; + } + + async render() { + // copy imageslider styles to the wrapper too + this.block.parentElement.classList.add( + ...[...this.block.classList].filter((item, idx) => idx !== 0 && item !== 'block'), + ); + + let defaultCSSPromise; + if (Array.isArray(this.cssFiles) && this.cssFiles.length > 0) { + // add default imageslider classes to apply default CSS + defaultCSSPromise = new Promise((resolve) => { + this.cssFiles.forEach((cssFile) => { + loadCSS(cssFile, (e) => resolve(e)); + }); + }); + this.block.parentElement.classList.add('imageslider-wrapper'); + this.block.classList.add('imageslider'); + } + + this.block.parentElement.classList.add('outer'); + this.block.innerHTML = ''; + const mainContainer = document.createElement('div'); + mainContainer.classList.add('imageslider-main'); + this.block.parentElement.prepend(mainContainer); + + this.data.forEach((item, index) => { + const itemContainer = document.createElement('div'); + itemContainer.classList.add('imageslider-item', `imageslider-item-${index + 1}`); + + let renderedItem = this.cardRenderer.renderItem(item); + renderedItem = Array.isArray(renderedItem) ? renderedItem : [renderedItem]; + renderedItem.forEach((renderedItemElement) => { + // There may be items in the imageslider that are skipped from scrolling + if (renderedItemElement.classList.contains('imageslider-skip-item')) { + itemContainer.classList.add('skip'); + } + itemContainer.appendChild(renderedItemElement); + }); + const mainClone = itemContainer.cloneNode(true); + mainContainer.appendChild(mainClone); + this.block.appendChild(itemContainer); + }); + + // set initial selected imageslider item + const activeItems = this.block.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + activeItems[this.currentIndex].classList.add('selected'); + + const activeMainItems = this.block.parentElement.firstChild.querySelectorAll('.imageslider-item:not(.clone,.skip)'); + activeMainItems[this.currentIndex].classList.add('selected'); + + // create autoscrolling animation + this.autoScroll && this.infiniteScroll + && (this.intervalId = setInterval(() => { this.nextItem(); }, this.autoScrollInterval)); + this.dotButtons && this.createDotButtons(); + this.counter && this.createCounter(); + this.navButtons && this.createNavButtons(this.block.parentElement); + this.infiniteScroll && this.createClones(); + this.addSwipeCapability(); + this.infiniteScroll && this.setInitialScrollingPosition(); + this.cssFiles && (await defaultCSSPromise); + this.clickthumbnails(); + this.setSliderWidth(); + this.setInitialImage(); + } +} + +/** + * Create and render default imageslider. + * Best practice: Create a new block and call the function, instead using or modifying this. + * @param {Element} block required - target block + * @param {Array} data optional - a list of data elements. + * either a list of objects or a list of divs. + * if not provided: the div children of the block are used + * @param {Object} config optional - config object for + * customizing the rendering and behaviour + */ +export async function createimageslider(block, data, config) { + const imageslider = new ImageSlider(block, data, config); + await imageslider.render(); + return imageslider; +} + +/** + * Custom card style config and rendering of imageslider items. + */ +export function renderCardItem(item) { + item.classList.add('card'); + item + .querySelectorAll('.button-container a') + .forEach((a) => a.append(span({ class: 'icon icon-chevron-right-outline', 'aria-hidden': true }))); + decorateIcons(item); + return item; +} + +const cardStyleConfig = { + cssFiles: ['/blocks/imageslider/imageslider-cards.css'], + navButtons: true, + dotButtons: false, + infiniteScroll: true, + autoScroll: false, + visibleItems: [ + { + items: 1, + condition: () => window.innerWidth < 768, + }, + { + items: 2, + condition: () => window.innerWidth < 1200, + }, { + items: 3, + }, + ], + renderItem: renderCardItem, +}; + +export default async function decorate(block) { + // cards style imageslider + const useCardsStyle = block.classList.contains('cards'); + if (useCardsStyle) { + await createimageslider(block, [...block.children], cardStyleConfig); + return; + } + + // use the default imageslider + await createimageslider(block); +}