Carousels
Carousels show a collection of items one at a time. They are also known as “slideshows” and “sliders”. Typical uses of carousels include scrolling news headlines, featured articles on home pages, and image galleries.
Resources
{#
Usage:
- css_class: (string)
Possible set of classes in addition to `ecl-carousel` wrapper.
- extra_attributes: (iterable)
Conventional map of possible extra attributes for the wrapper.
Pass any attribute, except: id, aria-labelledby
- carousel_id: (string)
The id of the carousel.
Defaults to `ecl-carousel`.
- carousel_aria_labelledby: (string)
The value for `aria-labelledby` attribute for of heeading of the carousel.
Defaults to `ecl-carousel__heading`.
This value MUST be the same as `heading_id`.
- heading_attributes: (iterable)
Conventional map of possible extra attributes for the header.
Pass any attribute, except: id, class.
- heading_id: (string)
The id of carousel heading.
Defaults to `ecl-carousel__heading`.
This value MUST be the same as `carousel_aria_labelledby`.
- heading_title: (string)
Carousel heading value.
Defaults to `gallery`.
- carousel_images (iterable)
A list of images following the structure:
{
image: {
src: '<path_to_image>',
alt: 'Image item alt text',
},
download: {
target: '#',
title: 'Download',
label: 'Download',
},
share: {
target: '#',
title: 'Share',
label: 'Share',
},
description: (string or block).
copyright: '© Copyright 1',
}
#}
{# Internal properties #}
{% set _css_class = 'ecl-carousel' %}
{% set _extra_attributes = '' %}
{% set _carousel_id = carousel_id|default('ecl-carousel') %}
{% set _carousel_aria_labellby = carousel_aria_labellby|default('ecl-carousel__heading') %}
{% set _heading_id = heading_id|default('ecl-carousel__heading') %}
{% set _heading_title = heading_title|default('gallery') %}
{# Internal logic - Process properties #}
{# Carousel classes #}
{% if css_class is defined %}
{% set _css_class = _css_class ~ ' ' ~ css_class %}
{% endif %}
{# Carousel attributes, except id and aria-labelledby #}
{% if extra_attributes is defined %}
{% for attr in extra_attributes %}
{% if attr != 'id' and attr != 'aria-labelledby' %}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ attr.name ~ '="' ~ attr.value ~ '"' %}
{% endif %}
{% endfor %}
{% endif %}
{# Carousel id and aria-labelledby connecting to heading #}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ 'id="' ~ _carousel_id ~ '"' %}
{% set _extra_attributes = _extra_attributes ~ ' ' ~ 'aria-labelledby="' ~ _carousel_aria_labellby ~ '"' %}
{# Heading attributes except the id #}
{% if heading_attributes is defined %}
{% for heading_attr in heading_attributes %}
{% if heading_attr != 'id' %}
{% set _heading_attributes = _heading_attributes ~ ' ' ~ heading_attr.name ~ '="' ~ heading_attr.value ~ '"' %}
{% endif %}
{% endfor %}
{% endif %}
{# Heading id #}
{% set _heading_attributes = _heading_attributes ~ 'id="' ~ _heading_id ~ '"' %}
{# Print result #}
<section class="{{ _css_class }}" {{ _extra_attributes|raw }}>
<h3 class="ecl-headings ecl-headings--h3 ecl-u-sr-only" {{ _heading_attributes }} >{{ _heading_title }}</h3>
<div class="ecl-carousel__list-wrapper">
<ul class="ecl-carousel__list ecl-list--unstyled">
{% for carousel_image in carousel_images %}
<li class="ecl-carousel__item">
<img src="{{ carousel_image.image.src }}" alt="{{ carousel_image.image.alt }}" class="ecl-carousel__image" />
</li>
{% endfor %}
</ul>
</div>
<div class="ecl-carousel__live-region">
{% for carousel_image in carousel_images %}
<div class="ecl-carousel__image-information" data-image={{ loop.index0 }}>
<div class="ecl-carousel__meta">
<span class="ecl-carousel__meta-item ecl-carousel__meta-download">
{% include '@ec-europa/ecl-links' with {
'extra_classes': 'ecl-carousel__meta-link ecl-icon ecl-icon--download',
'extra_attributes': [ 'name': 'title', 'value': carousel_image.download.title ],
'variant': ['inverted', 'standalone'],
}|merge(carousel_image.download)
%}
</span>
<span class="ecl-carousel__meta-item ecl-carousel__meta-share">
{% include '@ec-europa/ecl-links' with {
'extra_classes': 'ecl-carousel__meta-link ecl-icon ecl-icon--share',
'extra_attributes': [ 'name': 'title', 'value': carousel_image.share.title ],
'variant': ['inverted', 'standalone'],
}|merge(carousel_image.share)
%}
</span>
</div>
<div class="ecl-carousel__image-description">
{% block description %}
{{ carousel_image.description }}
{% endblock %}
</div>
<div class="ecl-carousel__image-copyright">
{{ carousel_image.copyright }}
</div>
</div>
{% endfor %}
</div>
</section>
{
"_demo": {
"scripts": "\n document.addEventListener('DOMContentLoaded', function () {\n ECL.carousels();\n });\n "
},
"carousel_images": [
{
"image": {
"src": "../../assets/demo_images/business-demo-1.jpg",
"alt": "First item"
},
"download": {
"href": "#",
"title": "Download",
"label": "Download"
},
"share": {
"href": "#",
"title": "Share",
"label": "Share"
},
"description": "<p>Nulla consequat massa quis enim. Donec pede justo.</p>\n <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href=\"#\" title=\"Cras dapibus\">Cras dapibus</a>. Vivamus elementum semper nisi.</p>",
"copyright": "© Copyright 1"
},
{
"image": {
"src": "../../assets/demo_images/business-demo-2.jpg",
"alt": "Second item"
},
"download": {
"href": "#",
"title": "Download",
"label": "Download"
},
"share": {
"href": "#",
"title": "Share",
"label": "Share"
},
"description": "<p>Nulla consequat massa quis enim. Donec pede justo.</p>\n <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href=\"#\" title=\"Cras dapibus\">Cras dapibus</a>. Vivamus elementum semper nisi.</p>",
"copyright": "© Copyright 2"
},
{
"image": {
"src": "../../assets/demo_images/business-demo-3.jpg",
"alt": "Third item"
},
"download": {
"href": "#",
"title": "Download",
"label": "Download",
"variant": [
"inverted",
"standalone"
]
},
"share": {
"href": "#",
"title": "Share",
"label": "Share",
"variant": [
"inverted",
"standalone"
]
},
"description": "<p>Nulla consequat massa quis enim. Donec pede justo.</p>\n <p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href=\"#\" title=\"Cras dapibus\">Cras dapibus</a>. Vivamus elementum semper nisi.</p>",
"copyright": "© Copyright 3"
}
]
}
<section class="ecl-carousel" id="ecl-carousel" aria-labelledby="ecl-carousel__heading">
<h3 class="ecl-headings ecl-headings--h3 ecl-u-sr-only" id="ecl-carousel__heading">gallery</h3>
<div class="ecl-carousel__list-wrapper">
<ul class="ecl-carousel__list ecl-list--unstyled">
<li class="ecl-carousel__item">
<img src="../../assets/demo_images/business-demo-1.jpg" alt="First item" class="ecl-carousel__image" />
</li>
<li class="ecl-carousel__item">
<img src="../../assets/demo_images/business-demo-2.jpg" alt="Second item" class="ecl-carousel__image" />
</li>
<li class="ecl-carousel__item">
<img src="../../assets/demo_images/business-demo-3.jpg" alt="Third item" class="ecl-carousel__image" />
</li>
</ul>
</div>
<div class="ecl-carousel__live-region">
<div class="ecl-carousel__image-information" data-image=0>
<div class="ecl-carousel__meta">
<span class="ecl-carousel__meta-item ecl-carousel__meta-download">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--download" href="#" =":" ="" =":" ="">Download</a>
</span>
<span class="ecl-carousel__meta-item ecl-carousel__meta-share">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--share" href="#" =":" ="" =":" ="">Share</a>
</span>
</div>
<div class="ecl-carousel__image-description">
<p>Nulla consequat massa quis enim. Donec pede justo.</p>
<p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href="#" title="Cras dapibus">Cras dapibus</a>. Vivamus elementum semper nisi.</p>
</div>
<div class="ecl-carousel__image-copyright">
© Copyright 1
</div>
</div>
<div class="ecl-carousel__image-information" data-image=1>
<div class="ecl-carousel__meta">
<span class="ecl-carousel__meta-item ecl-carousel__meta-download">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--download" href="#" =":" ="" =":" ="">Download</a>
</span>
<span class="ecl-carousel__meta-item ecl-carousel__meta-share">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--share" href="#" =":" ="" =":" ="">Share</a>
</span>
</div>
<div class="ecl-carousel__image-description">
<p>Nulla consequat massa quis enim. Donec pede justo.</p>
<p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href="#" title="Cras dapibus">Cras dapibus</a>. Vivamus elementum semper nisi.</p>
</div>
<div class="ecl-carousel__image-copyright">
© Copyright 2
</div>
</div>
<div class="ecl-carousel__image-information" data-image=2>
<div class="ecl-carousel__meta">
<span class="ecl-carousel__meta-item ecl-carousel__meta-download">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--download" href="#" =":" ="" =":" ="">Download</a>
</span>
<span class="ecl-carousel__meta-item ecl-carousel__meta-share">
<a class="ecl-link ecl-link--inverted ecl-link--standalone ecl-carousel__meta-link ecl-icon ecl-icon--share" href="#" =":" ="" =":" ="">Share</a>
</span>
</div>
<div class="ecl-carousel__image-description">
<p>Nulla consequat massa quis enim. Donec pede justo.</p>
<p>In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Integer tincidunt. <a href="#" title="Cras dapibus">Cras dapibus</a>. Vivamus elementum semper nisi.</p>
</div>
<div class="ecl-carousel__image-copyright">
© Copyright 3
</div>
</div>
</div>
</section>
-
Content:
import { queryAll } from '@ec-europa/ecl-base/helpers/dom'; import debounce from 'lodash.debounce'; /** * @param {object} options Object containing configuration overrides */ export const carousels = ({ selectorId: selectorId = 'ecl-carousel' } = {}) => { // SUPPORTS if (!('querySelector' in document) || !('addEventListener' in window)) { return null; } // SETUP let currentSlide = 0; const carousel = document.getElementById(selectorId); const slides = queryAll('.ecl-carousel__item', carousel); const list = carousel.querySelector('.ecl-carousel__list'); function getListItemWidth() { return carousel.querySelector('.ecl-carousel__item').offsetWidth; } function goToSlide(n) { slides[currentSlide].classList.remove('ecl-carousel__item--showing'); currentSlide = (n + slides.length) % slides.length; slides[currentSlide].classList.add('ecl-carousel__item--showing'); } function setOffset() { const itemWidth = getListItemWidth(); const tr = `translate3d(${-currentSlide * itemWidth}px, 0, 0)`; list.style.MozTransform = tr; /* FF */ list.style.msTransform = tr; /* IE (9+) */ list.style.OTransform = tr; /* Opera */ list.style.WebkitTransform = tr; /* Safari + Chrome */ list.style.transform = tr; } function announceCurrentSlide() { carousel.querySelector( '.ecl-carousel__meta-slide' ).textContent = `${currentSlide + 1} / ${slides.length}`; } function showImageInformation() { // Reset/Hide all. const infoAreas = queryAll('[data-image]'); // If anything is visible. if (infoAreas) { // eslint-disable-next-line infoAreas.forEach(area => (area.style.display = 'none')); } carousel.querySelector(`[data-image="${currentSlide}"]`).style.display = 'block'; } function previousSlide() { goToSlide(currentSlide - 1); setOffset(); announceCurrentSlide(); showImageInformation(); } function nextSlide() { goToSlide(currentSlide + 1); setOffset(); announceCurrentSlide(); showImageInformation(); } // Attach controls to a carousel. function addCarouselControls() { const navControls = document.createElement('ul'); navControls.className = 'ecl-carousel__controls ecl-list--unstyled'; navControls.innerHTML = ` <li> <button type="button" class="ecl-icon ecl-icon--left ecl-carousel__button ecl-carousel__button--previous"> <span class="ecl-u-sr-only">Previous</span></button> </li> <li> <button type="button" class="ecl-icon ecl-icon--right ecl-carousel__button ecl-carousel__button--next"> <span class="ecl-u-sr-only">Next</span> </button> </li> `; navControls .querySelector( '.ecl-carousel__button--previous', '.ecl-carousel__controls' ) .addEventListener('click', previousSlide); navControls .querySelector('.ecl-carousel__button--next', '.ecl-carousel__controls') .addEventListener('click', nextSlide); carousel .querySelector('.ecl-carousel__list-wrapper') .appendChild(navControls); } function removeCarouselControls() { const controls = carousel.querySelector('.ecl-carousel__controls'); carousel.querySelector('.ecl-carousel__list-wrapper').removeChild(controls); } function addLiveRegion() { const liveRegion = document.createElement('div'); liveRegion.setAttribute('aria-live', 'polite'); liveRegion.setAttribute('aria-atomic', 'true'); liveRegion.classList.add('ecl-carousel__meta-slide'); carousel .querySelector('.ecl-carousel__live-region') .appendChild(liveRegion); } function removeLiveRegion() { const liveRegion = carousel.querySelector('.ecl-carousel__meta-slide'); carousel .querySelector('.ecl-carousel__live-region') .removeChild(liveRegion); } const debounceCb = () => debounce( () => { setOffset(); }, 100, { maxWait: 300 } )(); // INIT function init() { addCarouselControls(); addLiveRegion(); goToSlide(0); announceCurrentSlide(); showImageInformation(); // Re-align on resize. window.addEventListener('resize', debounceCb); } // DESTROY function destroy() { removeCarouselControls(); removeLiveRegion(); window.removeEventListener('resize', debounceCb); } init(); // REVEAL API return { init, destroy, }; }; // module exports export default carousels;
- URL: /components/raw/ecl-carousels/carousels.js
- Filesystem Path: framework/components/ecl-carousels/carousels.js
- Size: 4.5 KB
-
Content:
/** * Carousel * @define carousel ; weak */ .ecl-carousel { align-items: stretch; background-color: #000; display: flex; flex-direction: column; margin: 0; max-width: 100%; overflow: hidden; @include ecl-media-breakpoint-up('xl') { flex-direction: row; max-height: 100vh; } } // When carousel is a dialog. .ecl-carousel[aria-hidden='true'] { display: none; } .ecl-carousel[aria-hidden='false'] { display: flex; height: 90vh; left: 3%; position: absolute; top: 3%; width: 90%; z-index: map-get($ecl-z-index, 'modal'); } .ecl-carousel__list-wrapper { max-height: 70vh; overflow: hidden; position: relative; } @include ecl-media-breakpoint-up('xl') { .ecl-carousel__list-wrapper { flex: 1; max-height: 100%; } } .ecl-carousel__controls { margin: 0; } .ecl-carousel__list { display: flex; margin: 0; white-space: nowrap; width: 100%; } .ecl-carousel__item { flex: 1 0 100%; position: relative; } .ecl-carousel__image { display: block; margin: auto; } .ecl-carousel__button { @include ecl-focus-outline-border(); background-color: #000; border: 0; color: #fff; font-size: map-get($ecl-font-size, 'xxl'); position: absolute; top: 50%; transform: translateY(-50%); z-index: map-get($ecl-z-index, 'navigation'); } .ecl-carousel__button--previous { left: 0; } .ecl-carousel__button--next { right: 0; } // JS will show only the necessary one by a data attribute. .ecl-carousel__image-information { display: none; text-align: left; } .ecl-carousel__live-region { background-color: map-get($ecl-colors, 'grey-100'); color: #fff; min-width: 30%; padding: map-get($ecl-spacing, 'l'); a { color: #fff; } } @include ecl-media-breakpoint-up('xl') { .ecl-carousel__live-region { flex: 0; max-height: 100%; } } .ecl-carousel__meta { margin-bottom: map-get($ecl-spacing, 'l'); } // Every link has specific styling. .ecl-carousel__meta-link { padding-right: map-get($ecl-spacing, 'm'); position: relative; // put icon on right without extends &::before { position: absolute; right: 0; } } .ecl-carousel__meta-item { display: inline-block; margin-right: map-get($ecl-spacing, 'xs'); padding-bottom: map-get($ecl-spacing, 'xxxs'); } .ecl-carousel__meta-slide { order: -1; padding-top: map-get($ecl-spacing, 's'); } .ecl-carousel__image-copyright { font-size: map-get($ecl-font-size, 'xxs'); } /* Show information in a similar flow as if there were js. */ .no-js { .ecl-carousel__list-wrapper { overflow-x: initial; } .ecl-carousel__list { align-items: center; height: 100%; justify-content: flex-start; } .ecl-carousel__item { opacity: 1; } .ecl-carousel__image-information { border-bottom: 1px solid #fff; display: block; margin-bottom: map-get($ecl-spacing, 's'); padding-bottom: map-get($ecl-spacing, 's'); } .ecl-carousel__live-region { overflow-y: auto; } }
- URL: /components/raw/ecl-carousels/ecl-carousels.scss
- Filesystem Path: framework/components/ecl-carousels/ecl-carousels.scss
- Size: 3 KB
- Handle: @ec-europa/ecl-carousels
- Tags: molecule
- Preview:
- Filesystem Path: framework/components/ecl-carousels/ecl-carousels.twig