Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
113 changes: 76 additions & 37 deletions blocks/filter-cards/filter-cards.css
Original file line number Diff line number Diff line change
@@ -1,60 +1,99 @@
.filter-cards > ul {
box-sizing: border-box;
.filter-cards .filter-cards-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1.5rem;
width: 100%;
grid-template-columns: 1fr;
gap: 24px;
}

@media (width >= 600px) {
.filter-cards .filter-cards-list {
grid-template-columns: repeat(2, 1fr);
}
}

.filter-cards > ul > li {
box-sizing: border-box;
display: flex;
flex-direction: column;
min-width: 0;
overflow: hidden;
border: 1px solid var(--dark-color, #333);
border-radius: 4px;
background-color: var(--background-color);
@media (width >= 900px) {
.filter-cards .filter-cards-list {
grid-template-columns: repeat(3, 1fr);
}
}

.filter-cards .filter-cards-card-image {
flex: 0 0 auto;
.filter-cards-card {
background: transparent;
}

.filter-cards-card-image {
line-height: 0;
overflow: hidden;
margin-bottom: 12px;
}

.filter-cards .filter-cards-card-image picture,
.filter-cards > ul > li img {
.filter-cards-card-image a {
display: block;
}

.filter-cards-card-image img {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 0;
}

.filter-cards .filter-cards-card-body {
flex: 1 1 auto;
min-height: 0;
margin: 0;
padding: 1rem;
overflow: auto;
text-align: left;
.filter-cards-card-body h3 {
font-family: var(--body-font-family-semibold);
font-size: 18px;
font-weight: 600;
line-height: 1.3;
text-transform: uppercase;
margin: 0 0 8px;
}

.filter-cards-card-body h3 a {
color: #000;
text-decoration: none;
}

.filter-cards-card-body h3 a:hover {
text-decoration: underline;
}

.filter-cards-card-body p {
font-size: 16px;
line-height: 1.4;
color: #333;
margin: 0 0 8px;
}

.filter-cards .filter-cards-card-body :is(h3, h4) {
margin: 0 0 0.5rem;
.filter-cards-card-body .filter-cards-read-more {
font-family: var(--body-font-family-semibold);
font-size: var(--body-font-size-s);
font-size: 14px;
font-weight: 600;
line-height: 1.25;
color: var(--text-color);
text-transform: uppercase;
text-decoration: underline;
color: #0357b8;
}

.filter-cards .filter-cards-card-body p {
margin: 0;
font-size: var(--body-font-size-xs);
line-height: 1.4;
color: var(--text-color);
.filter-cards-card-body .filter-cards-read-more:hover {
color: #024a9e;
}

.filter-cards .filter-cards-more {
display: block;
margin: 32px auto 0;
padding: 14px 40px;
font-family: var(--body-font-family-semibold);
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.5px;
background-color: #000;
color: #fff;
border: none;
border-radius: 50px;
cursor: pointer;
transition: background-color 0.2s;
}

.filter-cards .filter-cards-more:hover {
background-color: #333;
}
155 changes: 134 additions & 21 deletions blocks/filter-cards/filter-cards.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,139 @@
import { createOptimizedPicture } from '../../scripts/aem.js';

export default function decorate(block) {
const ul = document.createElement('ul');
[...block.children].forEach((row) => {
const li = document.createElement('li');
while (row.firstElementChild) li.append(row.firstElementChild);
[...li.children].forEach((div) => {
if (div.children.length === 1 && div.querySelector('picture')) {
div.className = 'filter-cards-card-image';
} else {
div.className = 'filter-cards-card-body';
}
});
ul.append(li);
const INDEX_URL = '/en/home/improve/query-index.json';
const PAGE_SIZE = 6;

let allArticles = [];
let filteredArticles = [];
let currentPage = 0;

async function fetchIndex() {
const resp = await fetch(INDEX_URL);
const json = await resp.json();
return json.data || [];
}

function createCard(article) {
const card = document.createElement('li');
card.className = 'filter-cards-card';

if (article.image) {
const imageDiv = document.createElement('div');
imageDiv.className = 'filter-cards-card-image';
const a = document.createElement('a');
a.href = article.path;
const img = document.createElement('img');
img.src = article.image;
img.alt = article.title;
img.loading = 'lazy';
a.appendChild(img);
imageDiv.appendChild(a);
card.appendChild(imageDiv);
}

const body = document.createElement('div');
body.className = 'filter-cards-card-body';

const title = document.createElement('h3');
const titleLink = document.createElement('a');
titleLink.href = article.path;
titleLink.textContent = article.title;
title.appendChild(titleLink);
body.appendChild(title);

if (article.description) {
const desc = document.createElement('p');
desc.textContent = article.description;
body.appendChild(desc);
}

const readMore = document.createElement('p');
const readMoreLink = document.createElement('a');
readMoreLink.href = article.path;
readMoreLink.textContent = 'Read More';
readMoreLink.className = 'filter-cards-read-more';
readMore.appendChild(readMoreLink);
body.appendChild(readMore);

card.appendChild(body);
return card;
}

function renderCards(block, articles, append = false) {
let list = block.querySelector('.filter-cards-list');
if (!list || !append) {
list = document.createElement('ul');
list.className = 'filter-cards-list';
const existing = block.querySelector('.filter-cards-list');
if (existing) existing.remove();
block.insertBefore(list, block.querySelector('.filter-cards-more'));
}

articles.forEach((article) => {
list.appendChild(createCard(article));
});
}

function getPageArticles() {
const start = currentPage * PAGE_SIZE;
return filteredArticles.slice(start, start + PAGE_SIZE);
}

function updateMoreButton(block) {
const btn = block.querySelector('.filter-cards-more');
const totalShown = (currentPage + 1) * PAGE_SIZE;
if (totalShown >= filteredArticles.length) {
btn.style.display = 'none';
} else {
btn.style.display = '';
}
}

function filterArticles(filter) {
if (!filter) {
filteredArticles = [...allArticles];
} else {
const lowerFilter = filter.toLowerCase();
filteredArticles = allArticles.filter((a) => {
const content = (a.content || '').toLowerCase();
const title = (a.title || '').toLowerCase();
const tags = (a.tags || '').toLowerCase();
return content.includes(lowerFilter)
|| title.includes(lowerFilter)
|| tags.includes(lowerFilter);
});
}
}

export default async function decorate(block) {
block.textContent = '';

const moreBtn = document.createElement('button');
moreBtn.className = 'filter-cards-more';
moreBtn.textContent = 'MORE';
block.appendChild(moreBtn);

const data = await fetchIndex();
allArticles = data
.filter((a) => a.path && a.title && a.path !== window.location.pathname)
.sort((a, b) => (b.lastModified || 0) - (a.lastModified || 0));

ul.querySelectorAll('picture > img').forEach((img) => {
const pic = img.closest('picture');
if (pic) {
pic.replaceWith(createOptimizedPicture(img.src, img.alt, false, [{ width: '750' }]));
}
filteredArticles = [...allArticles];
currentPage = 0;

renderCards(block, getPageArticles());
updateMoreButton(block);

moreBtn.addEventListener('click', () => {
currentPage += 1;
renderCards(block, getPageArticles(), true);
updateMoreButton(block);
});

block.replaceChildren(ul);
// Listen for filter events from filter-bubble block
document.addEventListener('filter-change', (e) => {
const filter = e.detail?.filter || '';
filterArticles(filter);
currentPage = 0;
renderCards(block, getPageArticles());
updateMoreButton(block);
});
}
Loading