From 38739d4e1b19136c507c850767cfcecdaa131a1c Mon Sep 17 00:00:00 2001 From: Martin Leblanc <m26lebla@uwaterloo.ca> Date: Fri, 14 Feb 2025 11:45:46 -0500 Subject: [PATCH] ISTWCMS-5650: Add initial css and js for the flickity carousel --- .../03-layouts/carousel/_carousel.scss | 238 ++++----- .../03-layouts/carousel/carousel.twig | 6 +- .../04-components/banners/_banners.scss | 478 ++++++++++-------- src/patterns/04-components/banners/banners.js | 162 +++--- 4 files changed, 457 insertions(+), 427 deletions(-) diff --git a/src/patterns/03-layouts/carousel/_carousel.scss b/src/patterns/03-layouts/carousel/_carousel.scss index 7621bdf5..38757f5a 100644 --- a/src/patterns/03-layouts/carousel/_carousel.scss +++ b/src/patterns/03-layouts/carousel/_carousel.scss @@ -1,119 +1,119 @@ -@use '../../01-core' as *; - -.uw-carousel { - display: block; - position: relative; - width: 100%; -} - -.owl-carousel.owl-theme { - display: flex !important; - flex-flow: column; - - .owl-dots{ - margin-bottom: var(--size-1); - .owl-dot { - &:hover{ - span { - background: var(--gray-5) !important; - color: var(--gray-5) !important; - } - } - &.active{ - span { - background: var(--gray-5) !important; - color: var(--gray-5) !important; - } - } - } - } -} - -.owl-stage-outer { - margin-bottom: var(--size-2); - order: 1; -} - -// Style the carousel buttons. -.uw-owl-nav{ - - &.disabled { - display: none; - } - display: flex; - justify-content: center; - order: 3; - - button { - background: var(--gray-2); - color: var(--uw-black); - display: inline-block; - font-family: var(--font-condensedbook); - font-size: var(--font-size-1); - font-weight: 200; - letter-spacing: 0.055rem; - margin: 0 var(--size-1); - max-width: inherit; - padding: var(--size-105); - text-decoration: none; - text-transform: uppercase; - user-select: none; - vertical-align: middle; - white-space: nowrap; - width: auto; - - &:hover { - background: var(--gray-5) !important; - color: var(--uw-white) !important; - } - } -} - -.owl-dots { - order: 2; - - &.disabled { - display: none; - } - - button { - background: var(--gray-5); - color: var(--uw-white-1); - display: inline-block; - font-size: var(--font-size-1); - height: var(--size-4); - margin: 0 auto; - max-width: inherit; - padding: inherit; - text-decoration: none; - user-select: none; - vertical-align: middle; - white-space: nowrap; - width: var(--size-4); - - &:hover { - background-color: transparent; - } - } -} - -.owl-theme .owl-dots .owl-dot { - span{ - border-radius: 50%; - height: var(--size-2) !important; - width: var(--size-2) !important; - } - - &:hover{ - span { - background: var(--gray-5) !important; - color: var(--gray-5) !important; - } - } - &.active{ - span { - background: var(--gray-5) !important; - color: var(--gray-5) !important; - } - } -} +//@use '../../01-core' as *; +// +//.uw-carousel { +// display: block; +// position: relative; +// width: 100%; +//} +// +//.owl-carousel.owl-theme { +// display: flex !important; +// flex-flow: column; +// +// .owl-dots{ +// margin-bottom: var(--size-1); +// .owl-dot { +// &:hover{ +// span { +// background: var(--gray-5) !important; +// color: var(--gray-5) !important; +// } +// } +// &.active{ +// span { +// background: var(--gray-5) !important; +// color: var(--gray-5) !important; +// } +// } +// } +// } +//} +// +//.owl-stage-outer { +// margin-bottom: var(--size-2); +// order: 1; +//} +// +//// Style the carousel buttons. +//.uw-owl-nav{ +// +// &.disabled { +// display: none; +// } +// display: flex; +// justify-content: center; +// order: 3; +// +// button { +// background: var(--gray-2); +// color: var(--uw-black); +// display: inline-block; +// font-family: var(--font-condensedbook); +// font-size: var(--font-size-1); +// font-weight: 200; +// letter-spacing: 0.055rem; +// margin: 0 var(--size-1); +// max-width: inherit; +// padding: var(--size-105); +// text-decoration: none; +// text-transform: uppercase; +// user-select: none; +// vertical-align: middle; +// white-space: nowrap; +// width: auto; +// +// &:hover { +// background: var(--gray-5) !important; +// color: var(--uw-white) !important; +// } +// } +//} +// +//.owl-dots { +// order: 2; +// +// &.disabled { +// display: none; +// } +// +// button { +// background: var(--gray-5); +// color: var(--uw-white-1); +// display: inline-block; +// font-size: var(--font-size-1); +// height: var(--size-4); +// margin: 0 auto; +// max-width: inherit; +// padding: inherit; +// text-decoration: none; +// user-select: none; +// vertical-align: middle; +// white-space: nowrap; +// width: var(--size-4); +// +// &:hover { +// background-color: transparent; +// } +// } +//} +// +//.owl-theme .owl-dots .owl-dot { +// span{ +// border-radius: 50%; +// height: var(--size-2) !important; +// width: var(--size-2) !important; +// } +// +// &:hover{ +// span { +// background: var(--gray-5) !important; +// color: var(--gray-5) !important; +// } +// } +// &.active{ +// span { +// background: var(--gray-5) !important; +// color: var(--gray-5) !important; +// } +// } +//} diff --git a/src/patterns/03-layouts/carousel/carousel.twig b/src/patterns/03-layouts/carousel/carousel.twig index 62691857..e0e56481 100644 --- a/src/patterns/03-layouts/carousel/carousel.twig +++ b/src/patterns/03-layouts/carousel/carousel.twig @@ -1,6 +1,6 @@ {% if pattern_lab %} - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.theme.default.min.css"> +{# <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css">#} +{# <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.theme.default.min.css">#} {% endif %} {% if carousel_type == 'banner' %} @@ -8,7 +8,7 @@ {% else %} <div class="uw-carousel"> {% endif %} - <div class="owl-carousel owl-theme"> + <div class="carousel"> {% block content %} <div class="item">Item #1</div> <div class="item">Item #2</div> diff --git a/src/patterns/04-components/banners/_banners.scss b/src/patterns/04-components/banners/_banners.scss index 380f8ac1..dfddec1b 100644 --- a/src/patterns/04-components/banners/_banners.scss +++ b/src/patterns/04-components/banners/_banners.scss @@ -4,222 +4,290 @@ --banner-transition-speed: 400ms; } -.uw-carousel { - &__banner { - .animated { - animation-duration: var(--banner-transition-speed) !important; - } +//.uw-carousel { +// &__banner { +// .animated { +// animation-duration: var(--banner-transition-speed) !important; +// } +// +// // Used to control the display of the play-pause. +// &[data-autoplay="1"] { +// .uw-owl-nav__banner-control-wrap { +// display: block; +// } +// +// &.banner-single { +// .uw-owl-nav__banner-control-wrap { +// display: none; +// } +// } +// } +// +// &[data-autoplay="0"] { +// .uw-owl-nav__banner-control-wrap { +// display: none; +// } +// } +// +// .uw-owl-nav__banner-control-wrap { +// bottom: var(--size-5); +// display: none; +// font-size: var(--font-size-3); +// left: var(--size-1); +// order: 4; +// position: absolute; +// z-index: var(--layer-content); +// .uw-owl-nav__banner-control { +// @include button-reset(); +// box-sizing: border-box; +// display: block; +// height: var(--size-4); +// overflow: hidden; +// position: relative; +// width: var(--size-4); +// &.uw-play{ +// display: none; +// } +// +// .uw-icon{ +// display: block; +// height: var(--size-4); +// width: var(--size-4); +// } +// } +// } +// +// .owl-stage-outer { +// margin-bottom: 0; +// overflow: hidden; +// } +// +// .owl-carousel { +// .uw-owl-nav__dots { +// align-items: flex-start; +// background: var(--gray-5); +// border-bottom: 1px solid var(--gray-5); +// display: flex; +// &.disabled { +// display: none; +// } +// gap: var(--size-xs); +// order: 3; +// .owl-dot { +// @include button-reset(); +// background-color: var(--gray-2); +// color: var(--uw-white); +// height: var(--size-3); +// margin: 0; +// padding: 0; +// width: 100%; +// &:hover, +// &:focus { +// background-color: var(--gray-5); +// color: var(--uw-white); +// } +// &.active{ +// background-color: var(--uw-black); +// } +// } +// } +// +// .uw-owl-nav__prevnext{ +// // Replace with display: flex; when theming +// // other banners.styles. +// display: none; +// &.disabled { +// display: none; +// } +// flex-flow: column; +// opacity: 0; +// position: absolute; +// top: calc(50% - 6rem); +// transition: opacity 350ms ease-in-out 250ms; +// width: 100% !important; +// z-index: 1; +// .owl-prev, +// .owl-next { +// @include button-reset(); +// background: rgba(0, 0, 0, 0.5); +// height: var(--size-13); +// position: absolute; +// vertical-align: middle !important; +// width: var(--size-6); +// &::after { +// display: block; +// font-family: var(--font-system); +// font-size: var(--font-size-12); +// } +// } +// +// .owl-prev { +// align-self: flex-start; +// padding-left: var(--size-2) !important; +// &::after { +// content: "\2039"; +// } +// } +// +// .owl-next{ +// align-self: flex-end; +// padding-left: var(--size-2) !important; +// right: 0; +// &::after { +// content: "\203A"; +// } +// } +// } +// +// &:hover { +// .uw-owl-nav__prevnext { +// opacity: 1; +// transition: opacity 350ms ease-in-out 250ms; +// } +// } +// } +// } +//} - // Used to control the display of the play-pause. - &[data-autoplay="1"] { - .uw-owl-nav__banner-control-wrap { - display: block; - } +//.no-js { +// .uw-carousel { +// &__banner { +// &[data-autoplay="1"] { +// .uw-owl-nav__banner-control-wrap { +// display: none; +// } +// } +// } +// } +// +// &.js { +// .uw-carousel { +// &__banner { +// &[data-autoplay="1"] { +// .uw-owl-nav__banner-control-wrap { +// display: inherit; +// } +// &.banner-single{ +// .uw-owl-nav__banner-control-wrap { +// display: none; +// } +// } +// } +// } +// } +// .card__banner:target ~ .card__banner:last-of-type, +// .card__banner { +// display: inherit; +// } +// .card__banner:last-of-type, +// .card__banner:target { +// display: inherit; +// } +// } +// +// .uw-owl-nav__dots-js { +// align-items: flex-start; +// background: var(--gray-5); +// border-bottom: var(--size-xs) solid var(--gray-5); +// display: flex; +// gap: var(--size-xs); +// order: 3; +// .owl-dot { +// @include button-reset(); +// background-color: var(--gray-2); +// color: var(--uw-white); +// height: var(--size-3); +// margin: 0; +// padding: 0; +// width: 100%; +// &:hover, +// &:focus { +// background-color: var(--gray-5); +// color: var(--uw-white); +// } +// +// &.active { +// background-color: var(--uw-black); +// } +// } +// } +// +// .card__banner:target ~ .card__banner:last-of-type, +// .card__banner { +// display: none; +// } +// +// /* :last-child works, but .page:last-child will not */ +// .card__banner:last-of-type, +// .card__banner:target { +// display: block; +// } +//} - &.banner-single { - .uw-owl-nav__banner-control-wrap { - display: none; - } - } - } - - &[data-autoplay="0"] { - .uw-owl-nav__banner-control-wrap { - display: none; - } - } - - .uw-owl-nav__banner-control-wrap { - bottom: var(--size-5); - display: none; - font-size: var(--font-size-3); - left: var(--size-1); - order: 4; - position: absolute; - z-index: var(--layer-content); - .uw-owl-nav__banner-control { - @include button-reset(); - box-sizing: border-box; - display: block; - height: var(--size-4); - overflow: hidden; - position: relative; - width: var(--size-4); - &.uw-play{ - display: none; - } - - .uw-icon{ - display: block; - height: var(--size-4); - width: var(--size-4); - } - } - } - - .owl-stage-outer { - margin-bottom: 0; - overflow: hidden; - } - - .owl-carousel { - .uw-owl-nav__dots { - align-items: flex-start; - background: var(--gray-5); - border-bottom: 1px solid var(--gray-5); - display: flex; - &.disabled { - display: none; - } - gap: var(--size-xs); - order: 3; - .owl-dot { - @include button-reset(); - background-color: var(--gray-2); - color: var(--uw-white); - height: var(--size-3); - margin: 0; - padding: 0; - width: 100%; - &:hover, - &:focus { - background-color: var(--gray-5); - color: var(--uw-white); - } - &.active{ - background-color: var(--uw-black); - } - } - } - - .uw-owl-nav__prevnext{ - // Replace with display: flex; when theming - // other banners.styles. - display: none; - &.disabled { - display: none; - } - flex-flow: column; - opacity: 0; - position: absolute; - top: calc(50% - 6rem); - transition: opacity 350ms ease-in-out 250ms; - width: 100% !important; - z-index: 1; - .owl-prev, - .owl-next { - @include button-reset(); - background: rgba(0, 0, 0, 0.5); - height: var(--size-13); - position: absolute; - vertical-align: middle !important; - width: var(--size-6); - &::after { - display: block; - font-family: var(--font-system); - font-size: var(--font-size-12); - } - } +.carousel { + background: #FAFAFA; + opacity: 0; + -webkit-transition: opacity 0.4s; + transition: opacity 0.4s; +} - .owl-prev { - align-self: flex-start; - padding-left: var(--size-2) !important; - &::after { - content: "\2039"; - } - } +.carousel.is-hidden { + display: none; +} - .owl-next{ - align-self: flex-end; - padding-left: var(--size-2) !important; - right: 0; - &::after { - content: "\203A"; - } - } - } +.carousel.flickity-enabled { + opacity: 1; +} - &:hover { - .uw-owl-nav__prevnext { - opacity: 1; - transition: opacity 350ms ease-in-out 250ms; - } - } - } - } +.flickity-button { + background: transparent !important; +} +/* big previous & next buttons */ +.flickity-prev-next-button { + width: 100px !important; + height: 100px !important; +} +/* icon color */ +.flickity-button-icon { + fill: var(--uw-gold) !important; +} +/* hide disabled button */ +.flickity-button:disabled { + display: none !important; } -.no-js { - .uw-carousel { - &__banner { - &[data-autoplay="1"] { - .uw-owl-nav__banner-control-wrap { - display: none; - } - } - } +/* position dots up a bit */ +.flickity-page-dots { + bottom: -2.125rem !important; +} +/* dots are lines */ +.flickity-page-dots .dot { + height: 2rem !important; + min-width: 2rem !important; + @media(min-width: $screen-md) { + min-width: 5rem !important; } - - &.js { - .uw-carousel { - &__banner { - &[data-autoplay="1"] { - .uw-owl-nav__banner-control-wrap { - display: inherit; - } - &.banner-single{ - .uw-owl-nav__banner-control-wrap { - display: none; - } - } - } - } - } - .card__banner:target ~ .card__banner:last-of-type, - .card__banner { - display: inherit; - } - .card__banner:last-of-type, - .card__banner:target { - display: inherit; - } + @media(min-width: $screen-lg) { + min-width: 10rem !important; } + width: 100%; + margin: 0 !important; + border-radius: 0 !important; +} - .uw-owl-nav__dots-js { - align-items: flex-start; - background: var(--gray-5); - border-bottom: var(--size-xs) solid var(--gray-5); - display: flex; - gap: var(--size-xs); - order: 3; - .owl-dot { - @include button-reset(); - background-color: var(--gray-2); - color: var(--uw-white); - height: var(--size-3); - margin: 0; - padding: 0; - width: 100%; - &:hover, - &:focus { - background-color: var(--gray-5); - color: var(--uw-white); - } - - &.active { - background-color: var(--uw-black); - } - } - } +.flickity-enabled:focus .flickity-viewport { + outline: thin dotted !important; + outline: 5px auto -webkit-focus-ring-color !important; +} - .card__banner:target ~ .card__banner:last-of-type, - .card__banner { - display: none; +.flickity-button:focus { + outline: none; + box-shadow: 0 0 0 5px var(--uw-black) !important; + .flickity-button-icon { + fill: var(--uw-black) !important; } +} - /* :last-child works, but .page:last-child will not */ - .card__banner:last-of-type, - .card__banner:target { - display: block; - } +.carousel.is-fullscreen .card__banner { + height: 100%; } diff --git a/src/patterns/04-components/banners/banners.js b/src/patterns/04-components/banners/banners.js index cd39547a..3afee57d 100644 --- a/src/patterns/04-components/banners/banners.js +++ b/src/patterns/04-components/banners/banners.js @@ -1,120 +1,82 @@ /** * @file + * Initializes Flickity carousels for banners in Drupal. + * Ensures autoplay, navigation, and pause/play functionality. */ -(function ($, Drupal) { +(function (Drupal) { 'use strict'; - Drupal.behaviors.banners = { - attach: function () { - $(document).ready(function () { - - // Step through each Banners on the page. - $('.uw-carousel__banner').each(function () { - - var selector = 'div[data-uuid="' + $(this).attr('data-uuid') + '"] .owl-carousel'; - - // Get banner carousel. - var owl = $(selector); - // Get the number of items for the carousel, if any. - // For banners we are only ever showing one item at - // a time (for reference Facts & Figures show more). - var numOfItems = 1; - - var bannerSlideSpeed = $(this).attr('data-slide-speed'); + Drupal.behaviors.bannersFlickity = { + attach: function () { + document.querySelectorAll('.uw-carousel__banner').forEach(function (banner) { + var selector = 'div[data-uuid="' + banner.getAttribute('data-uuid') + '"] .carousel'; + var elem = document.querySelector(selector); + if (!elem) { + return; + } - // The flag for autoplay. - var bannerAutoplay = true; + // Get the slide speed and autoplay settings. + var bannerSlideSpeed = parseInt(banner.getAttribute('data-slide-speed'), 10) || 3000; + var bannerAutoplay = banner.getAttribute('data-autoplay') !== '0' && !document.querySelector('.layout-builder'); - // If there is no autoplay, or we are in layout builder, - // set flag to false. - if ($(this).attr('data-autoplay') === 0 || $('.layout-builder').length) { - bannerAutoplay = false; - } + // Add a class if there's only one slide. + if (elem.children.length <= 1) { + banner.classList.add('banner-single'); + } - // Used to hide buttons when single banner - if (owl.children().length <= 1) { - $(this).addClass('banner-single'); - } + // Check if dots and navigation should be enabled. + var bannerDots = elem.children.length > 1; - // Get the loop value. - // Owl carousel setting to loop back to beginning. - var bannerLoop = false; - var bannerRewind = true; + // Ensure Flickity is defined before initializing. + if (typeof Flickity === 'undefined') { + console.error('Flickity is not defined. Ensure the library is loaded.'); + return; + } - // Get dots. - // Owl carousel setting show dots. - // If banner has more than 1 slide show dots. - var bannerDots = false; - if (owl.children().length > 1) { - bannerDots = true; - } - // Get nav. - // Owl carousel setting show prev next. - var bannerNav = false; - if ($(this).hasClass('uw-carousel__banner-inset')) { - bannerNav = true; - } + // Initialize Flickity carousel. + var flkty = new Flickity(elem, { + autoPlay: bannerAutoplay ? bannerSlideSpeed : false, + cellAlign: 'left', + contain: true, + draggable: true, + fullscreen: true, + cellSelector: '.card__banner', + pageDots: bannerDots + }); - // Get the play and stop buttons. - var bannerPlay = $(this).find($('.uw-play')); - var bannerPause = $(this).find($('.uw-pause')); + // Get play and pause buttons. + var bannerPlay = banner.querySelector('.uw-play'); + var bannerPause = banner.querySelector('.uw-pause'); - // Actions for play button. - bannerPlay.on('click',function () { - owl.trigger('play.owl.autoplay', [bannerSlideSpeed]); - bannerPlay.css('display', 'none'); - bannerPause.css('display', 'block'); + // Attach event listeners for play and pause. + if (bannerPlay && bannerPause) { + bannerPlay.addEventListener('click', function () { + flkty.playPlayer(); + bannerPlay.style.display = 'none'; + bannerPause.style.display = 'block'; }); - // Actions for pause button. - bannerPause.on('click',function () { - owl.trigger('stop.owl.autoplay'); - bannerPause.css('display', 'none'); - bannerPlay.css('display', 'block'); + bannerPause.addEventListener('click', function () { + flkty.stopPlayer(); + bannerPause.style.display = 'none'; + bannerPlay.style.display = 'block'; }); + } + }); - // Add the carousel to the banner using the id. - owl.owlCarousel({ - animateIn: 'fadeIn', - animateOut: 'fadeOut', - autoplay: bannerAutoplay, - autoHeight : true, - autoplayTimeout: bannerSlideSpeed, - autoplayHoverPause: true, - dots: bannerDots, - dotsClass: 'uw-owl-nav__dots', - loop: bannerLoop, - rewind: bannerRewind, - nav: bannerNav, - navContainerClass: 'uw-owl-nav__prevnext', - navText:[ - '', - '' - ], - responsiveClass: true, - responsive: { - 0: { - items: 1 - }, - 600: { - items: numOfItems <= 2 ? - (numOfItems - 1 > 0) ? - numOfItems - 1 : 1 : 2 - }, - 1000: { - items: numOfItems - } - }, - }); - }).on('click', '.owl-dot', function () { - // Pause the slideshow if they click to advance - // to a specific slide. We don't need to check - // if this is present because jQuery will just - // ignore this if it isn't. - $(this).closest('.uw-carousel__banner').find('.uw-pause').click(); - }); + // Pause carousel when clicking pagination dots. + document.addEventListener('click', function (event) { + if (event.target.matches('.flickity-page-dot')) { + var banner = event.target.closest('.uw-carousel__banner'); + if (banner) { + var pauseButton = banner.querySelector('.uw-pause'); + if (pauseButton) { + pauseButton.click(); + } + } + } }); } }; -})(jQuery, Drupal); +}(Drupal)); -- GitLab