Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accordion column block #114 #203

Merged
merged 16 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions blocks/v2-accordion-column/v2-accordion-column.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
.v2-accordion-column__item-header-button {
align-items: center;
background: transparent;
border: 0;
color: var(--c-primary-black);
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
margin: 0;
padding: 1em 0;
text-align: left;
width: 100%;
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
}

.v2-accordion-column__item-header-button:hover,
.v2-accordion-column__item-header-button:focus {
background: transparent;
cursor: pointer;
}

.v2-accordion-column__item-title {
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
font-family: var(--ff-headline-medium);
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
font-size: var(--headline-1-font-size);
letter-spacing: var(--headline-1-letter-spacing);
line-height: var(--headline-1-line-height);
margin: 0;
}

.v2-accordion-column__close {
transform: rotate(180deg);
transition: transform var(--duration-small) var(--easing-standard);
}

.v2-accordion-column__item .icon svg {
height: 24px;
width: 24px;
}

.v2-accordion-column__item .icon svg,
.v2-accordion-column__item a .icon svg {
display: flex;
}

.v2-accordion-column__item a .icon svg {
height: 16px;
width: 16px;
stroke: currentcolor;
}

.v2-accordion-column__item.active .v2-accordion-column__close {
transform: rotate(0);
}

.v2-accordion-column__item :is(
.v2-accordion-column__item-image,
.v2-accordion-column__item-description){
display: none;
}

.v2-accordion-column__item {
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
margin: 40px 0;
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
border-bottom: 2px solid var(--c-primary-black);
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
}

.v2-accordion-column__item.active {
display: flex;
flex-direction: column;
}

.v2-accordion-column__item.active :is(
.v2-accordion-column__item-image,
.v2-accordion-column__item-description) {
display: initial;
}

.v2-accordion-column__item-description .button-container .button {
align-items: stretch;
justify-content: flex-start;
gap: 4px;
margin: 0;
padding: 0;
border: 0;
background-color: transparent;
translate: none;
}

cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
.v2-accordion-column__item-description .button-container:hover .button svg,
.v2-accordion-column__item-description .button-container:focus-within .button svg{
translate: 3px;
transition: translate ease-out 0.2s;
}

.v2-accordion-column__item-image img {
aspect-ratio: 16/10;
width: 100%;
height: auto;
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
}
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved

/* Left variant */
.v2-accordion-column--left .v2-accordion-column__item-image {
order: 1;
}

.v2-accordion-column--left .v2-accordion-column__item-description {
order: 2;
}

cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
@media (min-width: 1200px) {
.v2-accordion-column__accordion-container {
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
display: flex;
width: 100vw;
max-width: 1440px;
position: relative;
left: 50%;
translate: -49.5%;
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
margin-top: 5em;
}

.v2-accordion-column__accordion-gap {
width: 50%;
height: 500px;
}

.v2-accordion-column__items-container {
width: 50%;
padding: 0 5em;
}

.v2-accordion-column__item-image {
position: absolute;
left: 0;
top: 0;
width: 50%;
}

.v2-accordion-column__item-title {
font-size: var(--body-font-size-xl);
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
}

/* Left variant */
.v2-accordion-column--left .v2-accordion-column__accordion-gap {
order: 2;
}

.v2-accordion-column--left .v2-accordion-column__items-container {
order: 1;
}

.v2-accordion-column--left .v2-accordion-column__item-image {
left: 50%;
}
}
79 changes: 79 additions & 0 deletions blocks/v2-accordion-column/v2-accordion-column.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createElement, variantsClassesToBEM } from '../../scripts/common.js';

const CLASSES = {
blockName: 'v2-accordion-column',
grayBackground: 'gray-background',
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved
left: 'left',
};

const { blockName, left, grayBackground } = CLASSES;
const variants = Object.values(CLASSES).splice(1);

const addAccordionClass = (item) => {
const hasPicture = item.querySelector('picture');
if (hasPicture) item.classList.add(`${blockName}__item-image`);
else {
const header = item.querySelector(':is(h1, h2, h3, h4, h5, h6)');
if (header) header.classList.add(`${blockName}__item-title`);
item.classList.add(`${blockName}__item-description`);
}
};

export default function decorate(block) {
const header = block.querySelector(':scope > div:first-child > div > :first-child');
const accordionItems = [...block.querySelectorAll(':scope > div:not(:first-child)')];
const accordionContainer = createElement('div', { classes: `${blockName}__accordion-container` });
const colGap = createElement('div', { classes: `${blockName}__accordion-gap` });
const itemsContainer = createElement('div', { classes: `${blockName}__items-container` });
const hasLeftClass = block.classList.contains(left); // accordion at left side
const hasGrayBgClass = block.classList.contains(grayBackground); // accordion with gray background
/** @type {boolean} */
const isLeftVariant = hasLeftClass
|| (!hasLeftClass && !!accordionItems[0].lastElementChild.querySelector('picture'));
// remove the gray variant and add it as section class
if (hasGrayBgClass) {
const section = 'section';
const sectionEl = block.closest(`.${section}`);
block.classList.remove(grayBackground);
sectionEl.classList.add(grayBackground);
variantsClassesToBEM(sectionEl.classList, variants, section);
}
if (!hasLeftClass && isLeftVariant) block.classList.add(left);
variantsClassesToBEM(block.classList, variants, blockName);
header.parentElement.classList.add(`${blockName}__header-wrapper`);
header.parentElement.parentElement.classList.add(`${blockName}__header-container`);

// style the header as an h2 with red marker over it
header.classList.remove('h1', 'h2', 'h3', 'h4', 'h5', 'h6');
header.classList.add(`${blockName}__header`, 'h2', 'with-marker');
cogniSyb marked this conversation as resolved.
Show resolved Hide resolved

// is responsibility of the author to add the proper amount of images and text
accordionItems.forEach((item, i) => {
const colBtnTitle = createElement('button', {
classes: `${blockName}__item-header-button`,
props: { type: 'button' },
});
const arrowEl = createElement('div', { classes: [`${blockName}__close`, 'icon'] });
const dropdownArrowIcon = document.createRange().createContextualFragment(`
<svg xmlns="http://www.w3.org/2000/svg"><use href="#icons-sprite-dropdown-caret"></use></svg>`);
const colItems = [...item.querySelectorAll(':scope > div')];

// add the proper classes to each accordion item
item.classList.add(`${blockName}__item`);
if (i === 0) item.classList.add('active');
colItems.forEach((col) => addAccordionClass(col));
arrowEl.appendChild(...dropdownArrowIcon.children);
colBtnTitle.prepend(item.querySelector(`.${blockName}__item-title`), arrowEl);
colBtnTitle.onclick = () => {
const active = accordionContainer.querySelector('.active');
if (active && active !== item) active.classList.remove('active');
item.classList.toggle('active');
};
item.prepend(colGap, colBtnTitle);
itemsContainer.appendChild(item);
});

accordionContainer.append(colGap, itemsContainer);

block.appendChild(accordionContainer);
}