-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(emulsif-304): add banner component
- Loading branch information
Showing
5 changed files
with
407 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
.banner { | ||
min-height: 16rem; | ||
position: relative; | ||
} | ||
|
||
.banner--has-image { | ||
background-size: 100%; | ||
background-position: center; | ||
} | ||
|
||
.banner__video-container { | ||
width: 100%; | ||
height: 100%; | ||
max-width: 100%; | ||
position: absolute; | ||
} | ||
|
||
.banner--has-image, | ||
.banner__video-container { | ||
&::before { | ||
content: ''; | ||
top: 0; | ||
left: 0; | ||
z-index: 1; | ||
width: 100%; | ||
height: 100%; | ||
opacity: 0.75; | ||
display: block; | ||
position: absolute; | ||
pointer-events: none; | ||
mix-blend-mode: multiply; | ||
background-color: var(--color-primary-dark); | ||
} | ||
} | ||
|
||
.banner__video { | ||
top: 0; | ||
left: 0; | ||
width: 100%; | ||
height: 100%; | ||
object-fit: cover; | ||
position: absolute; | ||
} | ||
|
||
.banner__video-controls { | ||
z-index: 3; | ||
position: absolute; | ||
bottom: var(--spacing-xl); | ||
right: var(--spacing-xl); | ||
} | ||
|
||
.banner__toggle { | ||
cursor: pointer; | ||
border-radius: 50%; | ||
width: var(--spacing-xl); | ||
height: var(--spacing-xl); | ||
border: 3px solid var(--color-white); | ||
background-color: var(--color-primary-lighter); | ||
|
||
svg { | ||
width: 100%; | ||
height: 100%; | ||
} | ||
} | ||
|
||
.banner__content { | ||
z-index: 2; | ||
width: 100%; | ||
display: flex; | ||
position: relative; | ||
flex-direction: column; | ||
color: var(--color-white); | ||
padding: var(--spacing-2xl) var(--spacing-xl); | ||
|
||
.heading { | ||
@include heading-large; | ||
|
||
margin: 0 0 calc(var(--spacing-lg) * 1.5); | ||
} | ||
|
||
p { | ||
margin: 0 0 var(--spacing-lg); | ||
font-size: var(--font-size-h5); | ||
|
||
&:is(p:last-child) { | ||
margin-bottom: 0; | ||
} | ||
} | ||
} | ||
|
||
.banner__action { | ||
margin: var(--spacing-xl) 0 0; | ||
|
||
.button, | ||
.button:hover, | ||
.button:focus { | ||
color: var(--color-black); | ||
font-size: var(--font-size-small); | ||
border-color: var(--color-primary-lighter); | ||
background-color: var(--color-primary-lighter); | ||
} | ||
} | ||
|
||
.banner-list { | ||
display: flex; | ||
flex-direction: column; | ||
row-gap: var(--spacing-xl); | ||
} | ||
|
||
/* Variants -- Content centered */ | ||
.banner__content--center { | ||
align-items: center; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
$schema: https://git.drupalcode.org/project/drupal/-/raw/10.1.x/core/modules/sdc/src/metadata.schema.json | ||
|
||
name: Banner | ||
group: Components | ||
status: stable | ||
props: | ||
type: object | ||
required: | ||
- banner__title | ||
- banner__content | ||
- banner__button_text | ||
- banner__button_url | ||
properties: | ||
banner__title: | ||
type: string | ||
title: Title | ||
description: 'Specifies the title of the banner' | ||
data: 'This is the banner title' | ||
banner__content: | ||
type: string | ||
title: Content | ||
description: 'Specifies the main content of the banner' | ||
data: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' | ||
banner__button_text: | ||
type: string | ||
title: Button Text | ||
description: 'Specifies the text displayed on the button' | ||
data: 'Learn More' | ||
banner__button_url: | ||
type: string | ||
title: Button URL | ||
description: 'Specifies the URL the button will link to' | ||
data: '#' | ||
banner__background_image: | ||
type: string | ||
title: Background Image | ||
description: 'Specifies the URL of the background image for the banner. Either this or banner__video should be provided.' | ||
data: 'https://example.com/path/to/background-image.jpg' | ||
banner__video: | ||
type: string | ||
title: Video URL | ||
description: 'Specifies the URL of the video to be displayed. Either this or banner__background_image should be provided.' | ||
data: '' | ||
banner__alignment: | ||
type: string | ||
title: Alignment | ||
description: 'Specifies the alignment of the content within the banner. Options include: left, center, right.' | ||
enum: | ||
- left | ||
- center | ||
data: 'center' | ||
example: | ||
- banner__title: 'THIS IS A BANNER HEADING' | ||
banner__content: '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>' | ||
banner__button_text: 'this is a link' | ||
banner__button_url: '#' | ||
banner__background_image: '' | ||
- banner__title: 'THIS IS A BANNER HEADING' | ||
banner__content: '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>' | ||
banner__button_text: 'this is a link' | ||
banner__button_url: '#' | ||
banner__video: '' | ||
- banner__title: 'THIS IS A BANNER HEADING' | ||
banner__content: '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>' | ||
banner__button_text: 'this is a link' | ||
banner__button_url: '#' | ||
banner__background_image: '' | ||
banner__alignment: 'center' | ||
- banner__title: 'THIS IS A BANNER HEADING' | ||
banner__content: '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>' | ||
banner__button_text: 'this is a link' | ||
banner__button_url: '#' | ||
banner__video: '' | ||
banner__alignment: 'center' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
Drupal.behaviors.banners = { | ||
attach(context) { | ||
const banners = context.querySelectorAll('.banner'); | ||
|
||
/** | ||
* getBannerReferences | ||
* | ||
* @description Returns references to banner elements. | ||
* @param {HTMLElement} banner Banner element. | ||
* @returns {Object} References to label, button, video, pause, and play elements. | ||
*/ | ||
function getBannerReferences(banner) { | ||
return { | ||
label: banner.querySelector('.banner__toggle_label'), | ||
button: banner.querySelector('.banner__toggle'), | ||
video: banner.querySelector('.banner__video'), | ||
pause: banner.querySelector('.banner__pause'), | ||
play: banner.querySelector('.banner__play'), | ||
}; | ||
} | ||
|
||
/** | ||
* playVideo | ||
* | ||
* @description Starts video playback and updates UI to reflect the playing state. | ||
* @param {Object} refs Object containing element references. | ||
* @returns {Promise} Resolves when the video starts playing or rejects with an error. | ||
*/ | ||
function playVideo(refs) { | ||
return new Promise((resolve, reject) => { | ||
refs.video | ||
.play() | ||
.then(() => { | ||
refs.pause.classList.remove('visually-hidden'); | ||
refs.play.classList.add('visually-hidden'); | ||
refs.label.textContent = 'Pause video'; | ||
resolve(); | ||
}) | ||
.catch((error) => { | ||
reject(error); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* pauseVideo | ||
* | ||
* @description Pauses video playback and updates UI to reflect the paused state. | ||
* @param {Object} refs Object containing element references. | ||
*/ | ||
function pauseVideo(refs) { | ||
refs.play.classList.remove('visually-hidden'); | ||
refs.pause.classList.add('visually-hidden'); | ||
refs.label.textContent = 'Play video'; | ||
refs.video.pause(); | ||
} | ||
|
||
/** | ||
* toggleVideo | ||
* | ||
* @description Toggles video playback state and updates UI accordingly. | ||
* @param {Object} refs Object containing element references. | ||
*/ | ||
function toggleVideo(refs) { | ||
if (refs.video.paused) { | ||
playVideo(refs); | ||
} else { | ||
pauseVideo(refs); | ||
} | ||
} | ||
|
||
/** | ||
* setInitialState | ||
* | ||
* @description Sets the initial state for a video element and performs associated actions. | ||
* @param {Object} refs Object containing element references. | ||
* @throws {DOMException} - If an error occurs while attempting to play the video. | ||
*/ | ||
async function setInitialState(refs) { | ||
const reduceMotion = window.matchMedia( | ||
'(prefers-reduced-motion: reduce)', | ||
); | ||
|
||
// Check if the user disabled 'prefer reduced motion' on their system. | ||
if (!reduceMotion.matches) { | ||
// Check if the user's browser allows video autoplay. | ||
try { | ||
await playVideo(refs); | ||
refs.actions[0].classList.add('hidden'); | ||
} catch (err) { | ||
if (err.name === 'NotAllowedError') { | ||
pauseVideo(refs); | ||
} | ||
} | ||
} else { | ||
pauseVideo(refs); | ||
} | ||
} | ||
|
||
banners?.forEach((banner) => { | ||
const refs = getBannerReferences(banner); | ||
|
||
if (refs.button && refs.video) { | ||
refs.button.addEventListener('click', () => toggleVideo(refs)); | ||
setInitialState(refs); | ||
} | ||
}); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import bannerTwig from './banner.twig'; | ||
import { props } from './banner.component.yml'; | ||
import bannerVideo from '../../media/video-placeholder.mp4'; | ||
import bannerImage from '../../images/example/banner-image.jpg'; | ||
import './banner'; | ||
|
||
export default { | ||
title: 'Components/Banner', | ||
decorators: [ | ||
(story) => | ||
`<div style="max-width: 890px; margin: 0 auto;">${story()}</div>`, | ||
], | ||
}; | ||
|
||
function getBannerData(data) { | ||
const newData = Object.assign({}, data); | ||
if (data && typeof data === 'object' && 'banner__video' in data) { | ||
newData.banner__video = bannerVideo; | ||
} | ||
if (data && typeof data === 'object' && 'banner__background_image' in data) { | ||
newData.banner__background_image = bannerImage; | ||
} | ||
return newData; | ||
} | ||
|
||
export const Banner = () => | ||
`<div class="banner-list">${props.example | ||
.map((data) => bannerTwig(getBannerData(data))) | ||
.join('')}</div>`; |
Oops, something went wrong.