Skip to content

Commit

Permalink
Merge pull request #55 from adobecom/MWPW-143411
Browse files Browse the repository at this point in the history
Migrate Toggle Bar Block
  • Loading branch information
vhargrave authored Sep 23, 2024
2 parents 64c2b75 + 3da3bf8 commit 51c1a74
Show file tree
Hide file tree
Showing 7 changed files with 13,820 additions and 0 deletions.
178 changes: 178 additions & 0 deletions express/blocks/toggle-bar/toggle-bar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
.section .toggle-bar-wrapper {
margin: 0 auto;
padding: 0 16px;
max-width: max-content;
}

.section[data-toggle]{
padding-top: 60px;
}

.toggle-bar {
display: none;
}

.toggle-bar.float {
margin-top: -72px;
}

.toggle-bar.sticky {
min-height: 147px;
position: relative;
z-index: 9;
}

.toggle-bar div[data-align="center"] {
margin: auto;
}

.toggle-bar div[data-align="center"] * {
text-align: center;
}

.toggle-bar.dark > div:first-of-type * {
color: var(--color-white);
}

.toggle-bar > div:nth-of-type(2) {
position: relative;
display: flex;
background: var(--color-white);
border-radius: 100px;
box-shadow: 0 0 10px #00000029;
box-sizing: border-box;
padding: 6px;
margin-top: 32px;
margin-bottom: 16px;
height: 70px;
overflow: auto clip;
transition: top 0.2s;
}

.toggle-bar.sticky.sticking > div:nth-of-type(2) {
position: fixed;
top: 0;
left: 50%;
transform: translate(-50%, 0);
}

.toggle-bar.sticky.sticking.bumped-by-gnav > div:nth-of-type(2) {
top: calc(var(--global-height-nav));
}

.toggle-bar.sticky.sticking.hidden > div:nth-of-type(2) {
top: -120px;
}

.toggle-bar > div:nth-of-type(2)::-webkit-scrollbar {
display: none;
}

.toggle-bar > div:nth-of-type(2) {
-ms-overflow-style: none;
scrollbar-width: none;
}

.toggle-bar > div {
display: flex;
align-items: center;
height: 100%;
}

.toggle-bar > div > div {
border-left: 2px solid #e8e8e8;
}

.toggle-bar > div > div:nth-of-type(1) {
border-left: unset;
}

.toggle-bar ul {
padding: 0;
margin: 0;
height: 100%;
display: flex;
justify-content: center;
}

.toggle-bar .toggle-bar-button {
position: relative;
display: inline-block;
list-style-type: none;
background-color: transparent;
font-size: var(--body-font-size-s);
margin: 0 28px;
cursor: pointer;
user-select: none;
border: none;
font-family: var(--body-font-family);
color: black;
}

.toggle-bar .toggle-bar-button:after {
content: '';
position: absolute;
left: 50%;
bottom: -13px;
transform: translate(-50%);
height: 4px;
width: 100%;
max-height: 0;
opacity: 0;
background-color: var(--color-info-accent);
border-radius: 100px 100px 0 0;
transition: max-height .2s, opacity .2s;
}

.toggle-bar .toggle-bar-button.active:after,
.toggle-bar .toggle-bar-button:hover:after {
max-height: 4px;
opacity: 1;
}

.toggle-bar .toggle-bar-button .text-wrapper {
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
transition: color .3s, font-weight .3s;
}

.toggle-bar .toggle-bar-button.active .text-wrapper {
font-weight: 700;
color: var(--color-info-accent);
}

.toggle-bar .toggle-bar-button .icons-wrapper .icon {
height: 18px;
width: 18px;
}

.toggle-bar .toggle-bar-button.active .icons-wrapper .icon:first-of-type,
.toggle-bar .toggle-bar-button .icons-wrapper .icon:nth-of-type(2) {
display: none;
}

.toggle-bar .toggle-bar-button .icons-wrapper .icon:first-of-type,
.toggle-bar .toggle-bar-button.active .icons-wrapper .icon:nth-of-type(2) {
display: unset;
}

.toggle-bar .tag {
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
font-size: var(--body-font-size-xs);
padding: 2px 4px;
font-weight: 700;
background-color: #DEDEF9;
color: var(--color-info-accent);
border-radius: 4px;
}

@media (min-width: 900px) {
.toggle-bar {
display: block;
}
}
178 changes: 178 additions & 0 deletions express/blocks/toggle-bar/toggle-bar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { getLibs, readBlockConfig } from '../../scripts/utils.js';
import { addTempWrapperDeprecated } from '../../scripts/utils/decorate.js';
import { sendEventToAnalytics, textToName } from '../../scripts/instrument.js';
import { fixIcons } from '../../scripts/utils/icons.js';

const { createTag } = await import(`${getLibs()}/utils/utils.js`);

function decorateButton(block, toggle) {
const button = createTag('button', { class: 'toggle-bar-button' });
const iconsWrapper = createTag('div', { class: 'icons-wrapper' });
const textWrapper = createTag('div', { class: 'text-wrapper' });
const icons = toggle.querySelectorAll('img');

const tagText = toggle.textContent.trim().match(/\[(.*?)\]/);

if (tagText) {
const [fullText, tagTextContent] = tagText;
const tag = createTag('span', { class: 'tag' });
textWrapper.textContent = toggle.textContent.trim().replace(fullText, '').trim();
button.dataset.text = textWrapper.textContent.toLowerCase();
tag.textContent = tagTextContent;
textWrapper.append(tag);
} else {
textWrapper.textContent = toggle.textContent.trim();
button.dataset.text = textWrapper.textContent.toLowerCase();
}

if (icons.length > 0) {
icons.forEach((icon) => {
iconsWrapper.append(icon);
});
}

button.append(iconsWrapper, textWrapper);
toggle.parentNode.replaceChild(button, toggle);

let texts = [];
let child = textWrapper.firstChild;
while (child) {
if (child.nodeType === 3) {
texts.push(child.data);
}
child = child.nextSibling;
}

texts = texts.join('') || textWrapper.textContent.trim();
const eventName = `adobe.com:express:homepage:intentToggle:${textToName(texts)}`;
button.addEventListener('click', () => {
sendEventToAnalytics(eventName);
});
}

function initButton(block, sections, index, props) {
const enclosingMain = block.closest('main');
if (enclosingMain) {
const buttons = block.querySelectorAll('.toggle-bar-button');

buttons[index].addEventListener('click', () => {
const activeButton = block.querySelector('button.active');
props.activeTab = buttons[index].dataset.text;

localStorage.setItem('createIntent', buttons[index].dataset.text);

if (activeButton !== buttons[index]) {
activeButton.classList.remove('active');
buttons[index].classList.add('active');

sections.forEach((section) => {
if (buttons[index].dataset.text === section.dataset.toggle.toLowerCase()) {
section.style.display = 'block';
props.activeSection = section;
} else {
section.style.display = 'none';
}
});
}

if (!block.classList.contains('sticky')) {
window.scrollTo({
top: Math.round(window.scrollY + block.getBoundingClientRect().top) - 24,
behavior: 'smooth',
});
}
});

if (index === 0) {
buttons[index].classList.add('active');
props.activeTab = buttons[index].dataset.text;
[props.activeSection] = sections;
}
}
}

function syncWithStoredIntent(block) {
const buttons = block.querySelectorAll('button');
const createIntent = localStorage.getItem('createIntent');

if (createIntent) {
const targetBtn = Array.from(buttons).find((btn) => btn.dataset.text === createIntent);
if (targetBtn) targetBtn.click();
}
}

function initStickyBehavior(block, props) {
const toggleBar = block.querySelector('div:nth-of-type(2)');
const gNav = document.querySelector('header.global-navigation');
const topValue = gNav ? 20 : -45;

if (toggleBar) {
document.addEventListener('scroll', () => {
const blockRect = block.getBoundingClientRect();
const sectionRect = props.activeSection && props.activeSection.getBoundingClientRect();

if (sectionRect && sectionRect.bottom < 0) {
block.classList.add('hidden');
} else if (blockRect.top < topValue) {
block.classList.remove('hidden');
block.classList.add('sticking');
if (gNav) block.classList.add('bumped-by-gnav');
} else if (blockRect.top >= topValue) {
block.classList.remove('sticking');
block.classList.remove('hidden');
block.classList.remove('bumped-by-gnav');
}
}, { passive: true });
}
}

function decorateSectionMetadata(section) {
const metadataDiv = section.querySelector(':scope > .section-metadata');

if (metadataDiv) {
const meta = readBlockConfig(metadataDiv);
const keys = Object.keys(meta);
keys.forEach((key) => {
if (!['style', 'anchor', 'background'].includes(key)) {
section.setAttribute(`data-${key}`, meta[key]);
}
});
}
}

function decorteSectionsMetadata() {
const sections = document.querySelectorAll('.section');
sections.forEach(decorateSectionMetadata);
}

export default async function decorate(block) {
await fixIcons(block);
addTempWrapperDeprecated(block, 'toggle-bar');
decorteSectionsMetadata();

const props = { activeTab: '', activeSection: null };
const enclosingMain = block.closest('main');
if (enclosingMain) {
const sections = enclosingMain.querySelectorAll('[data-toggle]');
const toggles = block.querySelectorAll('li');

toggles.forEach((toggle, index) => {
decorateButton(block, toggle);
initButton(block, sections, index, props);
});

if (sections) {
sections.forEach((section, index) => {
if (index > 0) {
section.style.display = 'none';
}
});
}

syncWithStoredIntent(block);

if (block.classList.contains('sticky')) {
initStickyBehavior(block, props);
}
}
}
Loading

0 comments on commit 51c1a74

Please sign in to comment.