diff --git a/blocks/card-list/card-list.js b/blocks/card-list/card-list.js index 4d944e338..e8fb13f1b 100644 --- a/blocks/card-list/card-list.js +++ b/blocks/card-list/card-list.js @@ -1,16 +1,17 @@ import ffetch from '../../scripts/ffetch.js'; import { - ul, a, div, span, h2, + ul, a, div, span, h2, button, input, label, } from '../../scripts/dom-builder.js'; import { toClassName } from '../../scripts/lib-franklin.js'; import createArticleCard from './articleCard.js'; import createApplicationCard from './applicationCard.js'; import createLibraryCard from './libraryCard.js'; +import { generateUUID, capitalize } from '../../scripts/scripts.js'; const getSelectionFromUrl = (field) => toClassName(new URLSearchParams(window.location.search).get(field)) || ''; -const createPaginationLink = (page, label, current = false) => { +const createPaginationLink = (page, btnLabel, current = false) => { const newUrl = new URL(window.location); newUrl.searchParams.set('page', page); const link = a( @@ -19,7 +20,7 @@ const createPaginationLink = (page, label, current = false) => { class: 'font-medium text-sm leading-5 pt-4 px-4 items-center inline-flex hover:border-t-2 hover:border-gray-300 hover:text-gray-700', }, - label || page, + btnLabel || page, ); if (current) { link.setAttribute('aria-current', 'page'); @@ -67,47 +68,100 @@ const createPagination = (entries, page, limit) => { return listPagination; }; -const createFilters = (articles, activeTag) => { +function toggleFilter(event) { + const isOpen = event.target.parentElement.getAttribute('aria-expanded'); + if (JSON.parse(isOpen)) { + event.target.parentElement.parentElement.focus(); + setTimeout(() => event.target.parentElement.parentElement.blur(), 200); + } else { + event.target.parentElement.parentElement.focus(); + } + event.target.parentElement.setAttribute('aria-expanded', !JSON.parse(isOpen)); +} + +const createFilters = (articles, activeTag, tagName) => { // collect tag filters - const allKeywords = articles.map((item) => item.topics.replace(/,\s*/g, ',').split(',')); + const allKeywords = articles.map((item) => item[tagName].replace(/,\s*/g, ',').split(',')); const keywords = new Set([].concat(...allKeywords)); keywords.delete(''); keywords.delete('Blog'); // filter out generic blog tag keywords.delete('News'); // filter out generic news tag - + let valSelected = ''; + [...keywords].forEach((keyword) => { + if (toClassName(keyword).toLowerCase() === activeTag) { + valSelected = `: ${keyword}`; + } + }); // render tag cloud const newUrl = new URL(window.location); - newUrl.searchParams.delete('tag'); + newUrl.searchParams.delete(tagName); newUrl.searchParams.delete('page'); + const uuid = generateUUID(); + const btnTopics = button({ + type: 'button', + class: 'btn px-4 rounded-full', + 'aria-expanded': false, + title: valSelected.replace(':', '').toString().trimStart(), + 'aria-controls': `${uuid}`, + }); + + valSelected = valSelected ? btnTopics.classList.add('btn-primary-purple', 'group') : btnTopics.classList.add('btn-outline-trending-brand', 'group'); + btnTopics.innerHTML = `${capitalize(tagName)} ${valSelected ? `${valSelected}` : ''} + + `; const tags = div( - { class: 'flex flex-wrap gap-2 mb-4' }, - a( - { - class: - 'text-center my-2 inline-block rounded-full px-4 py-2 font-semibold bg-d text-danaherpurple-500 bg-danaherpurple-50 hover:bg-gray-100 hover:text-gray-500', - href: newUrl.toString(), - }, - 'View All', + { + class: `dropdown group ${tagName} relative inline-block text-left pb-2 px-0 lg:px-2 pr-2`, + tabindex: '0', + }, + btnTopics, + ); + const dropdownDiv = div( + { id: `${uuid}`, class: 'dropdown-menu hidden group-focus-within:block w-max max-w-xs absolute center-0 z-10 mt-2 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none' }, + div( + { class: 'blog-inner-filter p-1 space-y-2', role: 'none' }, + a( + { class: 'flex gap-x-3 items-center text-gray-700 block px-4 py-2 text-sm hover:bg-slate-50', href: newUrl.toString().replace(`?${tagName}=all`, '').replace(`&${tagName}=all`, '') }, + input({ + class: 'view-all form-radio', type: 'radio', id: 'All', name: `${tagName}Radio`, value: 'All', + }), + label({ class: 'w-full text-sm font-medium text-gray-900', for: 'All' }, 'All'), + ), ), ); + + const dropdownDivInner = dropdownDiv.querySelector('div.blog-inner-filter'); + const allTag = dropdownDiv.querySelector('.view-all'); + allTag.setAttribute('checked', true); + allTag.addEventListener('click', (e) => { + window.location.href = e.target.parentElement.getAttribute('href'); + }); + [...keywords].sort().forEach((keyword) => { - newUrl.searchParams.set('tag', toClassName(keyword).toLowerCase()); - const tagAnchor = a( - { - class: - 'text-center my-2 inline-block rounded-full px-4 py-2 font-semibold bg-d hover:bg-gray-100 hover:text-gray-500', - href: newUrl.toString(), - }, - keyword, + newUrl.searchParams.set(tagName, toClassName(keyword).toLowerCase()); + const inputEl = input({ + class: 'form-radio', type: 'radio', id: `${keyword}`, name: `${tagName}Radio`, value: `${keyword}`, + }); + const tagsDiv = a( + { class: 'flex gap-x-3 items-center text-gray-700 block px-4 py-2 text-sm hover:bg-slate-50', href: newUrl.toString() }, + inputEl, + label({ class: 'w-full text-sm font-medium text-gray-900', for: `${keyword}` }, keyword), ); + inputEl.addEventListener('click', (e) => { + window.location.href = e.target.parentElement.getAttribute('href'); + }); if (toClassName(keyword).toLowerCase() === activeTag) { - tagAnchor.classList.add('bg-danaherpurple-500', 'text-white'); - tagAnchor.setAttribute('aria-current', 'tag'); + tagsDiv.setAttribute('aria-current', tagName); + inputEl.setAttribute('checked', true); + allTag.removeAttribute('checked'); } else { - tagAnchor.classList.add('text-danaherpurple-500', 'bg-danaherpurple-50'); + inputEl.removeAttribute('checked'); } - tags.append(tagAnchor); + dropdownDivInner.append(tagsDiv); + dropdownDiv.append(dropdownDivInner); + tags.append(dropdownDiv); }); + tags.addEventListener('click', toggleFilter); return tags; }; @@ -122,10 +176,18 @@ export default async function decorate(block) { .filter(({ type }) => type.toLowerCase() === articleType) .all(); let filteredArticles = articles; - const activeTagFilter = getSelectionFromUrl('tag'); - if (activeTagFilter) { + const activeTopicsFilter = getSelectionFromUrl('topics'); + const activeBrandFilter = getSelectionFromUrl('brand'); + + if (activeTopicsFilter) { filteredArticles = articles.filter( - (item) => toClassName(item.topics).toLowerCase().indexOf(activeTagFilter) > -1, + (item) => toClassName(item.topics).toLowerCase().indexOf(activeTopicsFilter) > -1, + ); + } + + if (activeBrandFilter) { + filteredArticles = filteredArticles.filter( + (item) => toClassName(item.brand).toLowerCase().indexOf(activeBrandFilter) > -1, ); } @@ -161,7 +223,7 @@ export default async function decorate(block) { block.append(divLetter, cardList); }); // render cards application style - } else if (articleType === 'application' || articleType === 'info') { + } else if (['application', 'info'].includes(articleType)) { filteredArticles.sort((card1, card2) => card1.title.localeCompare(card2.title)); const cardList = ul({ @@ -174,6 +236,7 @@ export default async function decorate(block) { block.append(cardList); // render cards article style } else { + // block.classList.add('space-x-2'); filteredArticles.sort((card1, card2) => card2.publishDate - card1.publishDate); let page = parseInt(getSelectionFromUrl('page'), 10); @@ -184,16 +247,17 @@ export default async function decorate(block) { const cardList = ul({ class: - 'container grid max-w-7xl w-full mx-auto gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 px-4 sm:px-0 justify-items-center mt-3 mb-3', + 'container grid max-w-7xl w-full mx-auto gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 px-0 lg:px-2 justify-items-center mt-4 mb-3', }); articlesToDisplay.forEach((article, index) => { cardList.appendChild(createArticleCard(article, index === 0)); }); // render pagination and filters - const filterTags = createFilters(articles, activeTagFilter); + const topicsFilters = createFilters(articles, activeTopicsFilter, 'topics'); + const brandFilters = createFilters(articles, activeBrandFilter, 'brand'); const paginationElements = createPagination(filteredArticles, page, limitPerPage); - block.append(filterTags, cardList, paginationElements); + block.append(topicsFilters, brandFilters, cardList, paginationElements); } } diff --git a/icons/icon-dropdown.svg b/icons/icon-dropdown.svg new file mode 100644 index 000000000..db3eded27 --- /dev/null +++ b/icons/icon-dropdown.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/scripts/scripts.js b/scripts/scripts.js index e43d18733..b6fc836bc 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -83,6 +83,15 @@ export function generateUUID() { return Math.floor(1000 + Math.random() * 9000); } +/** + * Regex to capitalize first letter every word + * @returns capitalized word +*/ +export function capitalize(str) { + const reg = /\b([a-zÁ-ú]{3,})/g; + return str.replace(reg, (w) => w.charAt(0).toUpperCase() + w.slice(1)); +} + /** * Returns the valid public url with or without .html extension * @param {string} url diff --git a/styles/styles.css b/styles/styles.css index af449d330..5c4119b3e 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -2139,6 +2139,36 @@ main .section.carousel-container { color: rgb(255 255 255 / var(--tw-text-opacity)); } +.form-radio { + height: 1.25rem; + width: 1.25rem; + flex-shrink: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 9999px; + border-width: 2px; + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.form-radio:checked { + border-color: transparent; + --tw-bg-opacity: 1; + background-color: rgb(51 51 51 / var(--tw-bg-opacity)); +} + +.form-radio:focus { + --tw-bg-opacity: 1; + background-color: rgb(51 51 51 / var(--tw-bg-opacity)); +} + +.form-radio[type="radio"]:checked { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E"); + background-size: 24px 24px; + background-position: center; + } + .sticky-footer { position: fixed; bottom: 0.75rem; @@ -3106,6 +3136,10 @@ main .default-content-wrapper ul { max-width: 80rem; } +.max-w-\[14rem\] { + max-width: 14rem; +} + .max-w-full { max-width: 100%; } @@ -3158,6 +3192,10 @@ main .default-content-wrapper ul { table-layout: auto; } +.origin-top-right { + transform-origin: top right; +} + .-translate-x-1\/2 { --tw-translate-x: -50%; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -3344,6 +3382,12 @@ main .default-content-wrapper ul { row-gap: 2rem; } +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + .space-x-3 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.75rem * var(--tw-space-x-reverse)); @@ -3434,6 +3478,12 @@ main .default-content-wrapper ul { overflow-x: auto; } +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .whitespace-nowrap { white-space: nowrap; } @@ -3681,6 +3731,14 @@ main .default-content-wrapper ul { fill: #fff; } +.stroke-danaherpurple-500 { + stroke: #7523FF; +} + +.stroke-white { + stroke: #fff; +} + .object-contain { -o-object-fit: contain; object-fit: contain; @@ -4276,10 +4334,30 @@ main .default-content-wrapper ul { box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) !important; } +.ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-black { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity)); +} + +.ring-opacity-5 { + --tw-ring-opacity: 0.05; +} + .\!ring-offset-0 { --tw-ring-offset-width: 0px !important; } +.blur { + --tw-blur: blur(8px); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + .invert { --tw-invert: invert(100%); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); @@ -4377,6 +4455,14 @@ main .section.top-border > div { padding-top: 1rem; } +/* .dropdown > .dropdown-menu { + @apply hidden; + } + + .dropdown:focus-within > .dropdown-menu { + @apply block; + } */ + @media (min-width: 768px) { .md\:btn-lg { @@ -4453,6 +4539,11 @@ main .section.top-border > div { background-color: rgb(125 86 164 / var(--tw-bg-opacity)); } +.hover\:bg-slate-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(248 250 252 / var(--tw-bg-opacity)); +} + .hover\:bg-slate-900\/\[0\.03\]:hover { background-color: rgb(15 23 42 / 0.03); } @@ -4537,6 +4628,10 @@ main .section.top-border > div { --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); } +.group:focus-within .group-focus-within\:block { + display: block; +} + .group:hover .group-hover\:translate-x-1 { --tw-translate-x: 0.25rem; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -4552,6 +4647,10 @@ main .section.top-border > div { background-color: rgb(117 35 255 / var(--tw-bg-opacity)); } +.group:hover .group-hover\:stroke-white { + stroke: #fff; +} + .group:hover .group-hover\:font-semibold { font-weight: 600; } @@ -5013,6 +5112,11 @@ main .section.top-border > div { padding-right: 2.5rem; } + .lg\:px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; + } + .lg\:px-7 { padding-left: 1.75rem; padding-right: 1.75rem; diff --git a/styles/tailwind.css b/styles/tailwind.css index d11347660..bf2bcec96 100644 --- a/styles/tailwind.css +++ b/styles/tailwind.css @@ -199,6 +199,16 @@ @apply py-2 text-lg; } + .form-radio{ + @apply w-5 h-5 appearance-none shrink-0 border-2 border-gray-300 rounded-full focus:bg-danaherblack-500 checked:bg-danaherblack-500 checked:border-transparent; + } + + .form-radio[type="radio"]:checked { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E"); + background-size: 24px 24px; + background-position: center; + } + .sticky-footer { @apply fixed w-full flex gap-x-2 justify-center bottom-3 px-5 transition z-10; } @@ -318,4 +328,12 @@ main .section.top-border > div { @apply pt-4; } + + /* .dropdown > .dropdown-menu { + @apply hidden; + } + + .dropdown:focus-within > .dropdown-menu { + @apply block; + } */ } diff --git a/tailwind.config.js b/tailwind.config.js index e61f0e00d..ceb0b7695 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,4 @@ +const plugin = require('tailwindcss/plugin') /** @type {import('tailwindcss').Config} */ module.exports = { content: ['./blocks/**/*.js', './scripts/*.js', '!./scripts/at-lsig.js', './fragments/*.html', './404.html'], // https://tailwindcss.com/docs/content-configuration#class-detection-in-depth @@ -7,6 +8,17 @@ module.exports = { opacity: ['disabled'], }, }, + plugins: [ + plugin(function ({ addVariant, e }) { + addVariant('aria-expanded', ({ modifySelectors, separator }) => { + modifySelectors(({ className }) => { + return `[aria-expanded='true'] + .${e( + `aria-expanded${separator}${className}` + )}` + }) + }) + }), + ], safelist: [ 'appear', 'btn',