<div class="carousel-container">
<div class="carousel" data-behavior="carousel">
<div>
<div style="border:2px solid; height: 300px;"> 1 </div>
</div>
<div>
<div style="border:2px solid; height: 300px;"> 2 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 3 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 4 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 5 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 6 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 7 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 8 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 9 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 10 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 11</div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 12 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 13 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 14 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 15 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 16 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 17 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 18 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 19 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 20 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 21 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 22 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 23 </div>
</div>
</div>
<div class="carousel-nav-block">
<div class="carousel-nav-wrapper">
<div role="tablist" class="carousel__pagination"></div>
<div class="carousel-navigation">
<button aria-label="Previous" class="carousel__cta carousel__prev"><span class="visually-hidden">Previous
page</span><svg fill="none" viewBox="0 0 10 17" xmlns="http://www.w3.org/2000/svg">
<path d="m9.2722 9.3355-6.75 6.75c-0.46089 0.4609-1.2087 0.4609-1.6706 0-0.46089-0.4609-0.46089-1.2097 0-1.6706l5.9146-5.9146-5.9146-5.9146c-0.46089-0.46089-0.46089-1.2097 0-1.6706 0.46194-0.46089 1.2097-0.46089 1.6706 0l6.75 6.75c0.46194 0.46089 0.46194 1.2097 0 1.6706v-1.4e-4z" fill="currentColor" />
</svg></button>
<button aria-label="Next" class="carousel__cta carousel__next"><span class="visually-hidden">Next
page</span><svg fill="none" viewBox="0 0 10 17" xmlns="http://www.w3.org/2000/svg">
<path d="m9.2722 9.3355-6.75 6.75c-0.46089 0.4609-1.2087 0.4609-1.6706 0-0.46089-0.4609-0.46089-1.2097 0-1.6706l5.9146-5.9146-5.9146-5.9146c-0.46089-0.46089-0.46089-1.2097 0-1.6706 0.46194-0.46089 1.2097-0.46089 1.6706 0l6.75 6.75c0.46194 0.46089 0.46194 1.2097 0 1.6706v-1.4e-4z" fill="currentColor" />
</svg></button>
</div>
</div>
</div>
</div>
No notes defined.
/* No context defined. */
import debounce from 'lodash.debounce';
import Glide from '@glidejs/glide';
export default ({ carouselGlideElement, responsive = [], ...options }) => {
const slides = carouselGlideElement.querySelectorAll('.glide__slides > li');
const carouselLoop = carouselGlideElement.getAttribute('data-carousel-loop');
const MOBILE_BREAKPOINT = 820;
let glide;
const glideType4 = slides.length <= 4 ? 'slider' : 'carousel';
const glideType2 = slides.length <= 2 ? 'slider' : 'carousel';
// eslint-disable-next-line no-unneeded-ternary
const isSwipe4 = slides.length <= 4 ? false : true;
// eslint-disable-next-line no-unneeded-ternary
const isSwipe2 = slides.length <= 2 ? false : true;
const breakpointSettings = {
500: {
perView: 1,
peek: { before: 40, after: 30 },
},
900: {
perView: 2,
type: glideType2,
swipeThreshold: isSwipe2,
dragThreshold: isSwipe2,
},
1200: {
perView: 4,
type: glideType4,
swipeThreshold: isSwipe4,
dragThreshold: isSwipe4,
},
...responsive,
};
const getCurrentSettings = () => {
const screenWidth = window.innerWidth;
// Because the `perView` changes at different breakpoints, find
// the relevant setting for the current screensize.
// Filter out any settings that are smaller than the current size, and
// then use the first in the list, as this will be the most relevant.
// If none are available, assume we are using the biggest setting.
// TODO: We should be able to reuse this, if we want to change the pagination
// to scroll full pages, rather than individual items
const breakpointSettingsForCurrentSize =
Object.keys(breakpointSettings).filter(
i => parseInt(i, 10) > screenWidth
)[0] || Object.keys(breakpointSettings).slice(-1);
return breakpointSettings[breakpointSettingsForCurrentSize];
};
const enablePrevButton = () => {
carouselGlideElement
.querySelector('.carousel__prev')
.classList.remove('disabled');
carouselGlideElement
.querySelector('.carousel__prev')
.removeAttribute('disabled');
};
const enableNextButton = () => {
carouselGlideElement
.querySelector('.carousel__next')
.classList.remove('disabled');
carouselGlideElement
.querySelector('.carousel__next')
.removeAttribute('disabled');
};
const disablePrevButton = () => {
carouselGlideElement
.querySelector('.carousel__prev')
.classList.add('disabled');
carouselGlideElement
.querySelector('.carousel__prev')
.setAttribute('disabled', true);
};
const disableNextButton = () => {
carouselGlideElement
.querySelector('.carousel__next')
.classList.add('disabled');
carouselGlideElement
.querySelector('.carousel__next')
.setAttribute('disabled', true);
};
const showPaginationByPage = () => {
// Glide shows a pagination button for each item, and doesn't provide
// an option to navigate whole pages at a time.
// This function hides extra individual dots, so that only the first item of
// each page is shown. (Based on the `perView` setting).
const { perView } = getCurrentSettings();
const paginationElements = carouselGlideElement.querySelectorAll(
`.glide-carousel__pagination button:not(:nth-child(${perView}n + 1))`
);
const visiblePaginationElements = carouselGlideElement.querySelectorAll(
`.glide-carousel__pagination button:nth-child(${perView}n + 1)`
);
paginationElements.forEach(item => {
item.classList.add('hidden');
});
visiblePaginationElements.forEach((item, index) => {
// eslint-disable-next-line no-param-reassign
item.ariaLabel = `Page ${index + 1} Button`;
});
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
return;
}
// disabling prev button by default
disablePrevButton();
};
const slideToNext = () => {
const maxPerView = getCurrentSettings().perView;
carouselGlideElement
.querySelector('.carousel__next')
.addEventListener('click', event => {
const { target } = event;
let activeDots = 0;
carouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
if (dot.getAttribute('class') === 'glide__bullet--active') {
activeDots = index;
}
});
const nextSlide = activeDots + maxPerView;
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
if (nextSlide > slides.length) {
glide.go(`=0`);
} else {
glide.go(`=${nextSlide}`);
}
} else {
glide.go(`=${nextSlide}`);
if (nextSlide + maxPerView > slides.length) {
target.closest('button').classList.add('disabled');
target.closest('button').setAttribute('disabled', true);
}
enablePrevButton();
}
});
};
const slideToPrevious = () => {
const maxPerView = getCurrentSettings().perView;
carouselGlideElement
.querySelector('.carousel__prev')
.addEventListener('click', event => {
const { target } = event;
let activeDots = 0;
carouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
if (dot.getAttribute('class') === 'glide__bullet--active') {
activeDots = index;
}
});
const prevSlide = activeDots - maxPerView;
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
if (prevSlide < 0) {
const lastslide =
Math.floor(slides.length / maxPerView) * maxPerView;
glide.go(`=${lastslide}`);
} else {
glide.go(`=${prevSlide}`);
}
} else {
if (prevSlide >= 0) {
glide.go(`=${prevSlide}`);
}
if (prevSlide - maxPerView < 0) {
target.closest('button').classList.add('disabled');
target.closest('button').setAttribute('disabled', true);
}
enableNextButton();
}
});
};
const enableNextPrev = () => {
const maxPerView = getCurrentSettings().perView;
carouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
dot.addEventListener('click', () => {
if (index > 0) {
enablePrevButton();
}
if (index === 0) {
enableNextButton();
disablePrevButton();
}
if (index + maxPerView > slides.length) {
disableNextButton();
}
if (index + maxPerView < slides.length) {
enableNextButton();
}
});
});
};
const showHideNavigation = () => {
const maxPerView = getCurrentSettings().perView;
const navigationElements = carouselGlideElement.querySelectorAll(
'[data-glide-dir]'
);
if (!slides || slides.length <= maxPerView) {
navigationElements.forEach(item => item.classList.add('hidden'));
// eslint-disable-next-line no-param-reassign
carouselGlideElement.querySelector('.carousel-navigation').style.display =
'none';
return;
}
navigationElements.forEach(item => {
item.classList.remove('hidden');
item.removeAttribute('aria-label');
});
showPaginationByPage();
// added manual arrow next/previous button action
slideToNext();
slideToPrevious();
enableNextPrev();
};
const createPagination = () => {
const bulletContainer = document.createElement('div');
bulletContainer.classList.add('glide-carousel__pagination');
bulletContainer.dataset.glideEl = 'controls[nav]';
slides.forEach((slide, index) => {
const button = document.createElement('button');
button.dataset.glideDir = `=${index}`;
bulletContainer.appendChild(button);
});
carouselGlideElement
.querySelector('.carousel-nav-wrapper')
.appendChild(bulletContainer);
showHideNavigation();
};
createPagination();
window.addEventListener(
'resize',
debounce(() => {
// Hide the navigation when we don't have more items than we can show on screen
showHideNavigation();
}, 250)
);
glide = new Glide(carouselGlideElement, {
perView: 4,
breakpoints: breakpointSettings,
type: glideType4,
swipeThreshold: isSwipe4,
dragThreshold: isSwipe4,
...options,
}).mount();
return glide;
};
import debounce from 'lodash.debounce';
import Glide from '@glidejs/glide';
export default ({ imageGalleryElement, responsive = [], ...options }) => {
const slides = imageGalleryElement.querySelectorAll('.glide__slides > li');
const images = imageGalleryElement.querySelectorAll('.image-gallery__img');
const totalSlides = imageGalleryElement.querySelector(
'.image-gallery__totalslides'
);
const navigation = imageGalleryElement.querySelector(
'.image-gallery__navigation'
);
const MOBILE_BREAKPOINT = 820;
let DEFAULT_SCREEN_WIDTH;
let glide = '';
let transformCount = 0;
const imgLink = imageGalleryElement.querySelectorAll('.image-gallery__link');
imgLink.forEach(link => {
if (link) {
link.removeAttribute('href');
}
});
const breakpointSettings = {
500: {
perView: 1,
peek: { before: 0, after: 0 },
},
954: {
perView: 1,
},
1200: {
perView: 1,
peek: { before: 'auto', after: 'auto' },
slideWidth: 954,
},
...responsive,
};
const getCurrentSettings = () => {
const screenWidth = window.innerWidth;
// Because the `perView` changes at different breakpoints, find
// the relevant setting for the current screensize.
// Filter out any settings that are smaller than the current size, and
// then use the first in the list, as this will be the most relevant.
// If none are available, assume we are using the biggest setting.
// TODO: We should be able to reuse this, if we want to change the pagination
// to scroll full pages, rather than individual items
const breakpointSettingsForCurrentSize =
Object.keys(breakpointSettings).filter(
i => parseInt(i, 10) > screenWidth
)[0] || Object.keys(breakpointSettings).slice(-1);
return breakpointSettings[breakpointSettingsForCurrentSize];
};
const updateBulletSize = (dot, action) => {
if (action === 'add') {
imageGalleryElement
.querySelector(`.glide-carousel__pagination > [data-slide-no='${dot}']`)
.classList.add('small');
} else {
imageGalleryElement
.querySelector(`.glide-carousel__pagination > [data-slide-no='${dot}']`)
.classList.remove('small');
}
};
const setBulletDefaultSize = () => {
const maxDots = 5;
const totalCount = slides.length;
const navigationElements = imageGalleryElement.querySelectorAll(
`.glide-carousel__pagination > [data-slide-no]`
);
if (totalCount > maxDots) {
navigationElements.forEach(item => {
item.classList.remove('small');
});
transformCount = 0;
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.glide-carousel__pagination'
).style.transform = 'translateX(0)';
updateBulletSize(4, 'add');
}
};
const setBulletSize = (currentSlide, nextSlide) => {
const maxDots = 5;
const transformXIntervalNext = -24;
const transformXIntervalPrev = 24;
const totalCount = slides.length;
if (totalCount > maxDots) {
if (nextSlide > currentSlide) {
if (
imageGalleryElement
.querySelector(
`.glide-carousel__pagination > [data-slide-no='${nextSlide}']`
)
.classList.contains('small')
) {
if (
!imageGalleryElement
.querySelector(
`.glide-carousel__pagination > [data-slide-no]:last-child`
)
.classList.contains('small')
) {
// eslint-disable-next-line operator-assignment
transformCount = transformCount + transformXIntervalNext;
updateBulletSize(nextSlide, 'remove');
const nextSlidePlusOne = nextSlide + 1;
updateBulletSize(nextSlidePlusOne, 'add');
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.glide-carousel__pagination'
).style.transform = `translateX(${transformCount}px)`;
const pPointer = nextSlide - 3;
const pPointerMinusOne = pPointer - 1;
updateBulletSize(pPointerMinusOne, 'remove');
updateBulletSize(pPointer, 'add');
}
} else {
// eslint-disable-next-line no-lonely-if
if (nextSlide === slides.length - 1) {
const navigationElements = imageGalleryElement.querySelectorAll(
`.glide-carousel__pagination > [data-slide-no]`
);
navigationElements.forEach(item => {
item.classList.remove('small');
});
updateBulletSize(slides.length - 5, 'add');
transformCount = (slides.length - 5) * transformXIntervalNext;
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.glide-carousel__pagination'
).style.transform = `translateX(${transformCount}px)`;
}
}
} else {
// eslint-disable-next-line no-lonely-if
if (
imageGalleryElement
.querySelector(
`.glide-carousel__pagination > [data-slide-no='${nextSlide}']`
)
.classList.contains('small')
) {
if (
!imageGalleryElement
.querySelector(
`.glide-carousel__pagination > [data-slide-no]:first-child`
)
.classList.contains('small')
) {
// eslint-disable-next-line operator-assignment
transformCount = transformCount + transformXIntervalPrev;
updateBulletSize(nextSlide, 'remove');
const nextSlidePlusOne = nextSlide - 1;
updateBulletSize(nextSlidePlusOne, 'add');
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.glide-carousel__pagination'
).style.transform = `translateX(${transformCount}px)`;
const nPointer = currentSlide + 3;
const nPointerMinusOne = nPointer - 1;
// console.log(`${nPointer} - ${nPointerMinusOne}`);
updateBulletSize(nPointer, 'remove');
updateBulletSize(nPointerMinusOne, 'add');
}
} else {
// eslint-disable-next-line no-lonely-if
if (nextSlide === 0) {
setBulletDefaultSize();
}
}
}
}
};
const countLeftPad = number => {
const slideCount = number.toString();
// eslint-disable-next-line no-undef
return slideCount.padStart(2, '0');
};
const setSlideCount = currentIndex => {
const slideCountBlock = imageGalleryElement.querySelector(
'.image-gallery__totalslides--count'
);
slideCountBlock.innerHTML = `${countLeftPad(
currentIndex + 1
)}/${countLeftPad(slides.length)}`;
};
const showPaginationByPage = () => {
// Glide shows a pagination button for each item, and doesn't provide
// an option to navigate whole pages at a time.
// This function hides extra individual dots, so that only the first item of
// each page is shown. (Based on the `perView` setting).
const { perView } = getCurrentSettings();
const paginationElements = imageGalleryElement.querySelectorAll(
`.glide-carousel__pagination button:not(:nth-child(${perView}n + 1))`
);
paginationElements.forEach(item => {
item.classList.add('hidden');
});
setSlideCount(0);
setBulletDefaultSize();
};
const showHideNavigation = () => {
const maxPerView = getCurrentSettings().perView;
const navigationElements = imageGalleryElement.querySelectorAll(
'[data-slide-no]'
);
if (!slides || slides.length <= maxPerView) {
navigationElements.forEach(item => item.classList.add('hidden'));
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.image-gallery__navigation'
).style.display = 'none';
// eslint-disable-next-line no-param-reassign
imageGalleryElement.querySelector(
'.image-gallery__totalslides'
).style.display = 'none';
imageGalleryElement
.querySelector('.glide__slides')
.classList.add('singleslide');
return;
}
navigationElements.forEach(item => item.classList.remove('hidden'));
showPaginationByPage();
};
const createPagination = () => {
const bulletContainer = document.createElement('div');
bulletContainer.classList.add('glide-carousel__pagination');
bulletContainer.dataset.glideEl = 'controls[nav]';
slides.forEach((slide, index) => {
const button = document.createElement('button');
button.dataset.slideNo = `${index}`;
button.tabIndex = '-1';
bulletContainer.appendChild(button);
});
imageGalleryElement
.querySelector('.carousel-nav-wrapper')
.appendChild(bulletContainer);
showHideNavigation();
};
const setPeekVal = () => {
let peekVal = 0;
const currentSlide = imageGalleryElement.querySelector(
'.glide__slide--active'
);
if (document.body.clientWidth > 954) {
peekVal = (document.body.clientWidth - 954) / 2;
}
glide.update({
peek: { before: peekVal, after: peekVal },
});
totalSlides.style.maxWidth = `${currentSlide.clientWidth}px`;
};
createPagination();
const formatUIElementsMobile = () => {
DEFAULT_SCREEN_WIDTH = window.innerWidth;
// Disabled for desktop
if (window.innerWidth >= MOBILE_BREAKPOINT) {
totalSlides.removeAttribute('style');
navigation.removeAttribute('style');
return;
}
const currentImage = images[glide.index];
const imageHeight = currentImage.offsetHeight;
totalSlides.style.position = 'absolute';
navigation.style.position = 'absolute';
totalSlides.style.top = `${16 + imageHeight}px`;
navigation.style.top = `${16 + imageHeight}px`;
};
glide = new Glide(imageGalleryElement, {
perView: 1,
type: 'slider',
breakpoints: breakpointSettings,
peek: { before: 100, after: 100 },
...options,
}).mount();
// fix for big screen when screen size bigger than carousel width
glide.on('build.after', () => {
if (document.body.clientWidth > slides.length * 954) {
const offsetVal = (954 + 10) / 2;
const carousel = imageGalleryElement.querySelector('.glide__slides');
carousel.style.transform = `translate3d(${offsetVal}px, 0px, 0px)`;
}
});
glide.on('run', () => {
const currentSlide = imageGalleryElement
.querySelector(
'.glide-carousel__pagination > [data-slide-no].glide__bullet--active'
)
.getAttribute('data-slide-no');
const nextSlide = glide.index;
setInterval(() => {
imgLink.forEach(link => {
if (link) {
link.removeAttribute('href');
}
});
}, 10);
// console.log(`${currentSlide} - ${nextSlide}`);
// eslint-disable-next-line radix
setBulletSize(parseInt(currentSlide), nextSlide);
setSlideCount(nextSlide);
updateBulletSize(nextSlide, 'remove');
// fix for big screen when screen size bigger than carousel width
if (document.body.clientWidth > slides.length * 954) {
let offsetVal = (954 + 10) / 2;
const carousel = imageGalleryElement.querySelector('.glide__slides');
offsetVal =
glide.index === slides.length - 1 ? `-${offsetVal}` : offsetVal;
carousel.style.transform = `translate3d(${offsetVal}px, 0px, 0px)`;
}
});
if (slides.length === 1) {
glide.update({
type: 'slider',
peek: { before: 0, after: 0 },
});
} else {
setPeekVal();
}
// If there is no description in image gallery component
const imageDesc = document.querySelector('.image-gallery__caption');
if (window.innerWidth <= MOBILE_BREAKPOINT) {
if (!imageDesc) {
const getCarouselPagination = document.querySelector(
'.image-gallery .carousel-nav-block'
);
getCarouselPagination.classList.add('align-pagination');
}
}
slides.forEach((slide, index) => {
slide.addEventListener('focus', () => {
glide.go(`=${index}`);
});
});
// run UI Function after Glidejs and other JS has loaded
window.addEventListener('load', () => {
formatUIElementsMobile();
});
window.addEventListener(
'resize',
debounce(() => {
// Hide the navigation when we don't have more items than we can show on screen
showHideNavigation();
setPeekVal();
if (DEFAULT_SCREEN_WIDTH !== window.innerWidth) {
formatUIElementsMobile();
}
}, 250)
);
return glide;
};
import Glider from '../../behaviours/carousel/glider/glider';
export default ({ carouselElement, responsive = [], ...options }) => {
const defaultOptions = {
slidesToShow: 1,
slidesToScroll: 1,
scrollLock: true,
draggable: true,
arrows: {
prev: '.carousel__prev',
next: '.carousel__next',
},
dots: '.carousel__pagination',
responsive: [
{
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
},
},
{
breakpoint: 900,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
},
},
{
breakpoint: 1200,
settings: {
slidesToShow: 4,
slidesToScroll: 4,
},
},
...responsive,
],
};
return new Glider(carouselElement, {
...defaultOptions,
...options,
});
};
.glide__slides {
margin: 0 auto;
> li {
display: flex;
justify-content: center;
}
}
[data-glide-el='track'] {
overflow: hidden;
}
.mobile-carousel {
[data-glide-el='track'] {
overflow-x: auto !important;
overflow-y: hidden !important;
width: auto !important;
}
.glide__slides {
overflow-x: auto !important;
overflow-y: hidden !important;
touch-action: initial !important;
padding-left: 3.2rem;
& > li {
&:first-child {
margin-left: 0;
}
margin: 0 0.6rem;
}
.article-session-card {
width: 174px;
min-width: 174px;
}
}
::-webkit-scrollbar {
width: 1px;
}
}
@mixin carousel-cta {
align-items: center;
appearance: none;
background-color: $grey;
border: 2px solid rgba(0, 0, 0, 0);
border-radius: 50%;
color: $black;
display: flex;
height: 0.8rem;
justify-content: center;
transition: background-color 0.2s ease-in;
transition: border 0.2s ease-in;
width: 0.8rem;
white-space: nowrap;
}
.glider-track {
margin: auto;
}
// TODO: Review this once more usecases established
// We should look to remove markup based selectors and
// this style might be better placed on individual usages.
.glider-slide > * {
margin-left: auto;
margin-right: auto;
}
.carousel-nav-block {
text-align: center;
padding-bottom: 2.5rem;
}
.carousel-nav-wrapper {
position: relative;
display: inline-flex;
align-items: center;
}
.carousel {
&-navigation {
display: flex;
justify-content: flex-end;
margin-bottom: 2rem;
}
&__cta {
@include text-s--narrow;
@include carousel-cta;
svg {
width: 0.8rem;
height: 1.5rem;
}
}
&__prev {
position: absolute;
left: -50px;
transform: rotate(180deg);
margin-right: $spacing-m;
width: 4.4rem !important;
height: 4.4rem !important;
border: 2px solid rgba(0, 0, 0, 255);
background-color: $white;
color: black;
&:hover {
border: 2px solid $blue !important;
}
}
&__next {
position: absolute;
right: -50px;
width: 4.4rem !important;
height: 4.4rem !important;
border: 2px solid rgba(0, 0, 0, 255);
background-color: $white;
color: black;
&:hover {
border: 1px solid $blue !important;
}
}
&__pagination {
display: flex;
justify-content: center;
margin-top: $spacing-xxl;
transition: all 0.25s;
button {
background-color: $grey;
border: none;
border-radius: 50%;
height: 0.8rem;
padding: 0;
margin: 0 $spacing-s;
width: 0.8rem;
&.active {
background-color: $blue;
}
&.glide__bullet--active {
background-color: $blue;
}
&.small {
transform: scale(0.5);
transition: transform 300ms;
}
&.inactive {
transform: scale(0);
transition: transform 300ms;
display: none;
}
}
}
@media screen and (min-width: $mq-medium) {
&__pagination {
counter-reset: pagination;
button {
@include carousel-cta;
@include text-m;
cursor: pointer;
&:not(.active):not(.glide__bullet--active) {
opacity: 0.7;
}
/* &::before {
counter-increment: pagination;
content: counter(pagination);
} */
}
}
&__cta {
@include text-s--narrow;
@include carousel-cta;
svg {
width: 0.8rem;
height: 1.5rem;
}
&:not(.disabled) {
cursor: pointer;
}
&:hover {
border: 2px solid $black;
}
&.disabled {
border: 2px solid $color-disabled !important;
background-color: $crest-blue !important;
color: $blue !important;
}
}
}
}
//
@media screen and (min-width: $mq-medium) {
.mobile-carousel {
.glide__slides {
.article-session-card {
width: auto;
min-width: initial;
}
}
}
.carousel-container {
max-width: 114rem;
}
.carousel-navigation {
margin-bottom: 0;
}
.carousel--desktop-reset {
.glider-track {
width: 100% !important; // important used to override js
flex-wrap: wrap;
}
[data-glide-el='track'] {
width: 100% !important; // important used to override js
flex-wrap: wrap;
}
.carousel {
&__pagination,
&-navigation {
display: none;
}
}
}
.carousel__prev {
position: absolute;
left: -75px;
}
.carousel__next {
position: absolute;
right: -75px;
}
}
@media (min-width: $mq-large) {
.glider-track .glider-slide {
float: left;
width: 25% !important;
}
}
.glide-carousel__pagination {
@extend .carousel__pagination;
}
.image-gallery {
.carousel-nav-wrapper {
width: 11.5rem;
overflow: hidden;
justify-content: center;
}
}
import debounce from 'lodash.debounce';
import Glide from '@glidejs/glide';
export default ({
fullWidthCarouselGlideElement,
responsive = [],
...options
}) => {
const slides = fullWidthCarouselGlideElement.querySelectorAll(
'.glide__slides > *'
);
const carouselLoop = fullWidthCarouselGlideElement.getAttribute(
'data-carousel-loop'
);
const paginationNavBlock = fullWidthCarouselGlideElement.querySelector(
'.full-width-carousel-nav-block'
);
const SMALL_MOBILE_BREAKPOINT = 500;
const MOBILE_BREAKPOINT = 820;
let glide;
let animationTime;
const defaultWidth = window.innerWidth;
if (window.innerWidth > MOBILE_BREAKPOINT) {
animationTime = 700;
} else {
animationTime = 200;
}
const breakpointSettings = {
500: {
perView: 2,
type: 'slider',
peek: 0,
gap: 12,
swipeThreshold: true,
dragThreshold: true,
},
744: {
perView: 2,
},
1200: {
perView: 3,
},
1366: {
perView: 4,
},
1500: {
perView: 4,
},
1921: {
perView: 5,
},
2200: {
perView: 6,
},
2321: {
perView: 7,
},
2560: {
perView: 8,
},
2632: {
perView: 10,
},
...responsive,
};
const visiblePipsOnFocus = () => {
if (!slides.length) {
return;
}
const pips = fullWidthCarouselGlideElement.querySelector(
'.glide-carousel__pagination'
);
const pipsArray = Array.from(pips.children);
pipsArray.forEach(child => {
const pip = child;
pip.onfocus = () => {
paginationNavBlock.style.opacity = '1';
};
});
};
const getCurrentSettings = () => {
const screenWidth = window.innerWidth;
// Because the `perView` changes at different breakpoints, find
// the relevant setting for the current screensize.
// Filter out any settings that are smaller than the current size, and
// then use the first in the list, as this will be the most relevant.
// If none are available, assume we are using the biggest setting.
// TODO: We should be able to reuse this, if we want to change the pagination
// to scroll full pages, rather than individual items
const breakpointSettingsForCurrentSize =
Object.keys(breakpointSettings).filter(
i => parseInt(i, 10) > screenWidth
)[0] || Object.keys(breakpointSettings).slice(-1);
return breakpointSettings[breakpointSettingsForCurrentSize];
};
const enablePrevButton = () => {
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__prev')
.classList.remove('disabled');
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__prev')
.removeAttribute('disabled');
};
const enableNextButton = () => {
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__next')
.classList.remove('disabled');
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__next')
.removeAttribute('disabled');
};
const disablePrevButton = () => {
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__prev')
.classList.add('disabled');
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__prev')
.setAttribute('disabled', true);
};
const disableNextButton = () => {
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__next')
.classList.add('disabled');
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__next')
.setAttribute('disabled', true);
};
const setSlidesWidth = glideSlides => {
if (!slides.length) {
return;
}
// setting the width of the full width wrapper component
const wrapper = slides[0].parentElement.parentElement;
wrapper.style.width = `${document.body.clientWidth}px`;
let slideWidth;
let gap;
const numberOfSlides = glideSlides.length;
const glideElement = fullWidthCarouselGlideElement.querySelector(
'.glide__slides'
);
if (window.innerWidth > MOBILE_BREAKPOINT) {
slideWidth = 274;
gap = 16;
glideElement.style.maxWidth = `${(slideWidth + gap) * numberOfSlides +
70}px`;
glideElement.style.minWidth = `${(slideWidth + gap) * numberOfSlides +
70}px`;
}
};
const showPaginationByPage = () => {
// Glide shows a pagination button for each item, and doesn't provide
// an option to navigate whole pages at a time.
// This function hides extra individual dots, so that only the first item of
// each page is shown. (Based on the `perView` setting).
const { perView } = getCurrentSettings();
const paginationElements = fullWidthCarouselGlideElement.querySelectorAll(
`.glide-carousel__pagination button:not(:nth-child(${perView}n + 1))`
);
const visiblePaginationElements = fullWidthCarouselGlideElement.querySelectorAll(
`.glide-carousel__pagination button:nth-child(${perView}n + 1)`
);
const titleElement = fullWidthCarouselGlideElement.parentElement.querySelector(
'h2'
);
let title = 'Full Width';
if (titleElement) {
title = titleElement.innerHTML;
}
paginationElements.forEach(item => {
item.classList.add('hidden');
});
visiblePaginationElements.forEach((item, index) => {
if (!index) {
// eslint-disable-next-line no-param-reassign
item.ariaLabel = `${title} Carousel Page - ${index + 1} active`;
} else {
// eslint-disable-next-line no-param-reassign
item.ariaLabel = `${title} Carousel Page - ${index + 1}`;
}
});
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
return;
}
setSlidesWidth(slides);
};
const slideToNext = () => {
const maxPerView = getCurrentSettings().perView;
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__next')
.addEventListener('click', event => {
const { target } = event;
let activeDots = 0;
fullWidthCarouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
if (dot.getAttribute('class') === 'glide__bullet--active') {
activeDots = index;
}
});
const nextSlide = activeDots + maxPerView;
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
if (nextSlide > slides.length) {
glide.go(`=0`);
} else {
glide.go(`=${nextSlide}`);
}
} else {
glide.go(`=${nextSlide}`);
if (nextSlide + maxPerView > slides.length) {
target.closest('button').classList.add('disabled');
target.closest('button').setAttribute('disabled', true);
}
enablePrevButton();
}
});
};
const slideToPrevious = () => {
const maxPerView = getCurrentSettings().perView;
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel__prev')
.addEventListener('click', event => {
const { target } = event;
let activeDots = 0;
fullWidthCarouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
if (dot.getAttribute('class') === 'glide__bullet--active') {
activeDots = index;
}
});
const prevSlide = activeDots - maxPerView;
if (carouselLoop && window.innerWidth > MOBILE_BREAKPOINT) {
if (prevSlide < 0) {
const lastslide =
Math.floor(slides.length / maxPerView) * maxPerView;
glide.go(`=${lastslide}`);
} else {
glide.go(`=${prevSlide}`);
}
} else {
if (prevSlide >= 0) {
glide.go(`=${prevSlide}`);
}
if (prevSlide - maxPerView < 0) {
target.closest('button').classList.add('disabled');
target.closest('button').setAttribute('disabled', true);
}
enableNextButton();
}
});
};
const enableNextPrev = () => {
const maxPerView = getCurrentSettings().perView;
fullWidthCarouselGlideElement
.querySelectorAll('.glide-carousel__pagination > *')
.forEach((dot, index) => {
dot.addEventListener('click', () => {
if (index > 0) {
enablePrevButton();
}
if (index === 0) {
enableNextButton();
disablePrevButton();
}
if (index + maxPerView > slides.length) {
disableNextButton();
}
if (index + maxPerView < slides.length) {
enableNextButton();
}
});
});
};
const showHideNavigation = () => {
const maxPerView = getCurrentSettings().perView;
const navigationElements = fullWidthCarouselGlideElement.querySelectorAll(
'[data-glide-dir]'
);
if (!slides || slides.length <= maxPerView) {
const glideElement = fullWidthCarouselGlideElement.querySelector(
'.glide__slides'
);
navigationElements.forEach(item => item.classList.add('hidden'));
// eslint-disable-next-line no-param-reassign
fullWidthCarouselGlideElement.querySelector(
'.full-width-carousel-nav-block'
).style.display = 'none';
glideElement.style.overflow = 'visible';
glideElement.style.margin = '0';
return;
}
navigationElements.forEach(item => {
item.classList.remove('hidden');
item.removeAttribute('aria-label');
});
showPaginationByPage();
// added manual arrow next/previous button action
slideToNext();
slideToPrevious();
enableNextPrev();
};
const createPagination = () => {
if (!slides.length) {
return;
}
const bulletContainer = document.createElement('div');
bulletContainer.classList.add('glide-carousel__pagination');
bulletContainer.dataset.glideEl = 'controls[nav]';
slides.forEach((slide, index) => {
const button = document.createElement('button');
button.dataset.glideDir = `=${index}`;
bulletContainer.appendChild(button);
});
fullWidthCarouselGlideElement
.querySelector('.full-width-carousel-nav-wrapper')
.appendChild(bulletContainer);
showHideNavigation();
// disabling prev button by default
disablePrevButton();
setSlidesWidth(slides);
if (window.innerWidth > MOBILE_BREAKPOINT) {
slides[0].parentElement.style.paddingLeft = '7rem';
} else {
slides[0].parentElement.style.paddingLeft = '3.2rem';
}
};
const updatePagination = glideIndex => {
const pips = fullWidthCarouselGlideElement.querySelector(
'.glide-carousel__pagination'
);
const pipsArray = Array.from(pips.children);
pipsArray.forEach((child, index) => {
const pip = child;
if (index === glideIndex) {
pip.ariaSelected = true;
// eslint-disable-next-line no-param-reassign
const label = pip.ariaLabel;
pip.ariaLabel = `${label} active`;
} else {
pip.ariaSelected = false;
if (pip.ariaLabel) {
const label = pip.ariaLabel.replace(' active', '');
// eslint-disable-next-line no-param-reassign
pip.ariaLabel = `${label}`;
}
}
});
};
createPagination();
window.addEventListener(
'resize',
debounce(() => {
// Hide the navigation when we don't have more items than we can show on screen
if (defaultWidth !== window.innerWidth) {
showHideNavigation();
setSlidesWidth(slides);
}
}, 250)
);
glide = new Glide(fullWidthCarouselGlideElement, {
perView: 4,
breakpoints: breakpointSettings,
type: 'slider',
swipeThreshold: false,
dragThreshold: false,
peek: 50,
gap: 16,
animationDuration: animationTime,
animationTimingFunc: 'ease',
...options,
}).mount();
visiblePipsOnFocus();
glide.on('run.after', () => {
const maxPerView = getCurrentSettings().perView;
if (window.innerWidth > SMALL_MOBILE_BREAKPOINT) {
if (glide.index < 1) {
enableNextButton();
if (window.innerWidth > MOBILE_BREAKPOINT) {
slides[0].parentElement.style.paddingLeft = '7rem';
} else {
slides[0].parentElement.style.paddingLeft = '3.2rem';
}
} else if (glide.index + maxPerView > slides.length - 1) {
disableNextButton();
slides[0].parentElement.style.paddingLeft = '0';
const slidesWidth = parseInt(
slides[0].parentElement.style.minWidth,
10
);
const slidesOffset = slidesWidth - document.body.clientWidth;
slides[0].parentElement.style.transform = `translate3d(-${slidesOffset}px, 0px, 0px)`;
} else {
enableNextButton();
slides[0].parentElement.style.paddingLeft = '0';
}
}
updatePagination(glide.index);
});
if (window.innerWidth <= MOBILE_BREAKPOINT) {
glide.destroy();
fullWidthCarouselGlideElement.classList.add('mobile-carousel');
}
return glide;
};
import { gsap } from 'gsap';
import debounce from 'lodash.debounce';
import Glide from '@glidejs/glide';
import { animationTime } from '../../modules/efl-get-started/efl-get-started';
const renderSlideDiv = (slideContainer, slides) => {
let slideCardContainer;
const MOBILE_BREAKPOINT = 768;
// eslint-disable-next-line no-param-reassign
slideContainer.innerHTML = '';
slides.forEach((slide, index) => {
const listElement = document.createElement('li');
if (!index) {
listElement.appendChild(slide);
slideContainer.appendChild(listElement);
} else {
const itemPos = index - 1;
let perView = 1;
if (window.innerWidth >= MOBILE_BREAKPOINT && window.innerWidth < 1045) {
perView = 2;
} else if (window.innerWidth > 1045) {
perView = 3;
}
if (itemPos === 0 || (index && itemPos % perView === 0)) {
const cardElement = document.createElement('div');
cardElement.className = 'card';
cardElement.appendChild(slide);
listElement.appendChild(cardElement);
slideContainer.appendChild(listElement);
slideCardContainer = cardElement;
} else if (slideCardContainer) {
slideCardContainer.appendChild(slide);
}
}
});
};
// get started become coach functions
export default ({ getStartedCarouselElement, responsive = [], ...options }) => {
const slides = getStartedCarouselElement.querySelectorAll(
'.glide__slides > *'
);
const contentOverlay = document.querySelector(
'.get-started-become-coach__overlay'
);
const MOBILE_BREAKPOINT = 768;
let glide;
let defaultWidth = window.innerWidth;
const slideContainer = getStartedCarouselElement.querySelector(
'.glide__slides'
);
const breakpointSettings = {
500: {
perView: 1,
type: 'slider',
peek: 0,
gap: 12,
swipeThreshold: true,
dragThreshold: true,
},
820: {
perView: 1,
},
...responsive,
};
window.addEventListener(
'resize',
debounce(() => {
// Hide the navigation when we don't have more items than we can show on screen
if (defaultWidth !== window.innerWidth) {
if (window.innerWidth < MOBILE_BREAKPOINT) {
glide.destroy();
getStartedCarouselElement.classList.add('mobile-carousel');
} else {
getStartedCarouselElement.classList.remove('mobile-carousel');
slideContainer.removeAttribute('style');
}
renderSlideDiv(slideContainer, slides);
defaultWidth = window.innerWidth;
}
}, 250)
);
gsap.set(contentOverlay, { y: 0, ease: 'none' });
renderSlideDiv(slideContainer, slides);
glide = new Glide(getStartedCarouselElement, {
perView: 1,
breakpoints: breakpointSettings,
type: 'slider',
swipeThreshold: false,
dragThreshold: false,
peek: 0,
gap: 16,
animationDuration: animationTime,
animationTimingFunc: 'cubic-bezier(0.16, 1, 0.3, 1)',
...options,
}).mount();
if (window.innerWidth < MOBILE_BREAKPOINT) {
glide.destroy();
getStartedCarouselElement.classList.add('mobile-carousel');
}
return glide;
};
export { renderSlideDiv };
<div class="carousel-container">
<div class="carousel" data-behavior="carousel">
<div>
<div style="border:2px solid; height: 300px;"> 1 </div>
</div>
<div>
<div style="border:2px solid; height: 300px;"> 2 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 3 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 4 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 5 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 6 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 7 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 8 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 9 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 10 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 11</div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 12 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 13 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 14 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 15 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 16 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 17 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 18 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 19 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 20 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 21 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 22 </div>
</div>
<div>
<div style="border:2px solid ; height: 300px;"> 23 </div>
</div>
</div>
<div class="carousel-nav-block">
<div class="carousel-nav-wrapper">
<div role="tablist" class="carousel__pagination"></div>
<div class="carousel-navigation">
<button aria-label="Previous" class="carousel__cta carousel__prev"><span class="visually-hidden">Previous
page</span><svg fill="none" viewBox="0 0 10 17" xmlns="http://www.w3.org/2000/svg">
<path
d="m9.2722 9.3355-6.75 6.75c-0.46089 0.4609-1.2087 0.4609-1.6706 0-0.46089-0.4609-0.46089-1.2097 0-1.6706l5.9146-5.9146-5.9146-5.9146c-0.46089-0.46089-0.46089-1.2097 0-1.6706 0.46194-0.46089 1.2097-0.46089 1.6706 0l6.75 6.75c0.46194 0.46089 0.46194 1.2097 0 1.6706v-1.4e-4z"
fill="currentColor" />
</svg></button>
<button aria-label="Next" class="carousel__cta carousel__next"><span class="visually-hidden">Next
page</span><svg fill="none" viewBox="0 0 10 17" xmlns="http://www.w3.org/2000/svg">
<path
d="m9.2722 9.3355-6.75 6.75c-0.46089 0.4609-1.2087 0.4609-1.6706 0-0.46089-0.4609-0.46089-1.2097 0-1.6706l5.9146-5.9146-5.9146-5.9146c-0.46089-0.46089-0.46089-1.2097 0-1.6706 0.46194-0.46089 1.2097-0.46089 1.6706 0l6.75 6.75c0.46194 0.46089 0.46194 1.2097 0 1.6706v-1.4e-4z"
fill="currentColor" />
</svg></button>
</div>
</div>
</div>
</div>