-
+ ${rubric ? `
${rubric}
-
+
` : ''}
${title}
@@ -49,44 +49,11 @@ const placeholderLoopCardHtml = ({
`;
};
-const placeholderTrendingItemHtml = ({
- image = '', imageAlt = 'alt-text', title = '', path = '#',
-} = {}) => `
-
-`;
-
const encodedUrl = encodeURIComponent(window.location.href);
const placeholderHtml = (data) => `
-
-
-
-
Trending ${trendingSvg}
-
- ${
- !data
- ? placeholderTrendingItemHtml().repeat(6)
- // TODO have trending query and use it instead of loop articles
- : data
- .slice(0, 6)
- .map((loopItem) => placeholderTrendingItemHtml(loopItem))
- .join('')
-}
-
-
-
+
+
`;
export default async function decorate(block) {
const id = getBlockId(block);
- let loopData;
+ await createAndInsertTrendingBannerBlock(block, 'trending');
// using placeholder html
- if (!loopData) {
- block.innerHTML = placeholderHtml();
- }
+ const placeholderTemplate = parseFragment(placeholderHtml());
+
+ render(placeholderTemplate, block);
+ block.innerHTML = '';
+ block.append(placeholderTemplate);
- // Rendering content upon fetch complete
+ // Re-rendering content upon fetch complete
document.addEventListener(`query:${id}`, (event) => {
- loopData = event.detail.data;
+ const loopData = event.detail.data;
const HTML_TEMPLATE = placeholderHtml(loopData);
// Template rendering
const template = parseFragment(HTML_TEMPLATE);
+
// Render template
render(template, block);
diff --git a/blocks/more-cards/more-cards.css b/blocks/more-cards/more-cards.css
index 43bfb3f..9145cbe 100644
--- a/blocks/more-cards/more-cards.css
+++ b/blocks/more-cards/more-cards.css
@@ -128,6 +128,10 @@
padding: 0 20px 20px;
}
+.more-cards div a:not(:has(span)) strong {
+ margin-top: 15px;
+}
+
.more-cards:not(.multiple) strong {
padding: 0 10px 10px;
}
diff --git a/blocks/trending-banner/trending-banner.css b/blocks/trending-banner/trending-banner.css
new file mode 100644
index 0000000..1e8529e
--- /dev/null
+++ b/blocks/trending-banner/trending-banner.css
@@ -0,0 +1,121 @@
+.trending-banner-wrapper .trending-banner.block .trending-wrapper {
+ width: 100%;
+ display: flex;
+ position: relative;
+ overflow-x: auto;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper a{
+ text-decoration: none;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-heading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ margin: 10px;
+ padding: 9px 15px;
+ width: 132px;
+ height: 28px;
+ z-index: 1;
+ background-color: #e8b94d;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-heading span {
+ font-size: 10px;
+ letter-spacing: 1px;
+ font-family: var(--font-gotham);
+ text-transform: uppercase;
+ font-weight: 500;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content {
+ list-style: none;
+ display: flex;
+ gap: 2px;
+ padding: 0;
+ margin: 0 auto;
+ position: relative;
+ background-color: white;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-item {
+ width: 220px;
+ height: 220px;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-item .trending-link {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ cursor: pointer;
+ position: relative;
+ overflow: hidden;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-text-wrapper {
+ width: 100%;
+ padding: 15px;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-text-wrapper .trending-title {
+ margin: 0;
+ font-weight: 400;
+ color: #fff;
+ font-size: 14px;
+ font-family: var(--font-gotham);
+ line-height: 130%;
+ position: relative;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-image-wrapper {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ display: flex;
+ position: absolute;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-image-wrapper picture {
+ width: 100%;
+ height: 100%;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-image-wrapper picture img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-image-wrapper:has(picture img)::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(180deg, transparent 30%, #000);
+}
+
+@media (max-width: 768px) {
+ .trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-content .trending-item {
+ width: 180px;
+ height: 180px;
+ }
+
+ .trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-heading {
+ width: 93px;
+ height: 22px;
+ padding: 7px 12px;
+ }
+
+ .trending-banner-wrapper .trending-banner.block .trending-wrapper .trending-heading span {
+ font-size: 8px;
+ }
+}
diff --git a/blocks/trending-banner/trending-banner.js b/blocks/trending-banner/trending-banner.js
new file mode 100644
index 0000000..5ed2419
--- /dev/null
+++ b/blocks/trending-banner/trending-banner.js
@@ -0,0 +1,74 @@
+import { createOptimizedPicture } from '../../scripts/lib-franklin.js';
+import {
+ getBlockId, parseFragment, removeEmptyElements, render,
+} from '../../scripts/scripts.js';
+
+const trendingSvg = '
';
+
+const placeholderTrendingItemHtml = ({
+ image = '', imageAlt = 'alt-text', title = '', path = '#',
+} = {}) => `
+
+`;
+
+const generateTemplate = (loopData) => `
+
+
+
Trending ${trendingSvg}
+ ${
+ !loopData
+ ? placeholderTrendingItemHtml().repeat(6)
+ // TODO have trending query and use it instead of loop articles
+ : loopData
+ .slice(0, 6)
+ .map((loopItem) => placeholderTrendingItemHtml(loopItem))
+ .join('')
+}
+
+
+
`;
+
+export default async function decorate(block) {
+ const id = getBlockId(block);
+
+ block.innerHTML = generateTemplate();
+
+ document.addEventListener(`query:${id}`, (event) => {
+ const HTML_TEMPLATE = generateTemplate(event.detail.data);
+
+ // block reference from function argument is no longer
+ // the node in the DOM, so we need to query it again.
+ const blockInDom = document.getElementById(id);
+
+ // Rendering
+ const template = parseFragment(HTML_TEMPLATE);
+
+ // Render template
+ render(template, block);
+
+ // Post-processing
+ removeEmptyElements(template, 'p');
+
+ blockInDom.innerHTML = '';
+ blockInDom.append(template);
+ });
+
+ // Trigger query
+ window.store.query(block);
+}
diff --git a/scripts/scripts.js b/scripts/scripts.js
index bf376ee..32b21d0 100644
--- a/scripts/scripts.js
+++ b/scripts/scripts.js
@@ -11,7 +11,7 @@ import {
waitForLCP,
loadBlocks,
loadCSS,
- toCamelCase, getMetadata, toClassName,
+ toCamelCase, getMetadata, toClassName, decorateBlock, loadBlock,
} from './lib-franklin.js';
export const ARTICLE_TEMPLATES = {
@@ -30,6 +30,17 @@ const LCP_BLOCKS = [...Object.values(ARTICLE_TEMPLATES), 'hero']; // add your LC
const range = document.createRange();
+// getting real path, and adjusting canonical link to use the vanity path
+const canonicalLinkTag = document.head.querySelector('link[rel="canonical"]');
+if (canonicalLinkTag) {
+ const longPathMetadata = document.createElement('meta');
+ longPathMetadata.setAttribute('property', 'hlx:long-form-path');
+ longPathMetadata.content = canonicalLinkTag?.href;
+ document.head.appendChild(longPathMetadata);
+ window.canonicalLocation = canonicalLinkTag.href;
+ canonicalLinkTag.href = window.location.href;
+}
+
export function replaceLinksWithEmbed(block) {
const embeds = ['youtube', 'brightcove', 'instagram', 'ceros'];
block.querySelectorAll(embeds.map((embed) => `a:only-child[href*="${embed}"]`).join(',')).forEach((embedLink) => {
@@ -217,28 +228,43 @@ function buildTemplate(main) {
// TODO remove once importer fixes more cards
const checkForMoreCards = (el, elems) => {
- if (el.tagName === 'P' && el.querySelector('picture') && el.querySelector('a') && el?.nextElementSibling?.tagName === 'P' && el.nextElementSibling.children[0]?.tagName === 'A' && el.nextElementSibling?.nextElementSibling.tagName === 'P' && el.nextElementSibling.nextElementSibling.children[0]?.tagName === 'A') {
+ if (el.tagName === 'P' && el.querySelector('picture') && el.querySelector('a')) {
+ const hasRubric = el.nextElementSibling.children[0]?.tagName === 'A';
+ const hasDesc = el.nextElementSibling?.nextElementSibling?.children[0]?.tagName === 'A';
+
const rubric = document.createElement('span');
- rubric.textContent = el.nextElementSibling.textContent.trim();
+ rubric.textContent = (hasRubric && hasDesc) ? el.nextElementSibling.textContent.trim() : '';
const desc = document.createElement('strong');
- desc.textContent = el.nextElementSibling.nextElementSibling.textContent.trim();
+ desc.textContent = '';
+ if (hasRubric && hasDesc) {
+ desc.textContent = el.nextElementSibling.nextElementSibling.textContent.trim();
+ } else if (hasRubric) {
+ desc.textContent = el.nextElementSibling.textContent.trim();
+ }
const link = document.createElement('a');
link.setAttribute('href', new URL(el.querySelector('a').getAttribute('href')).pathname);
link.append(el.querySelector('picture'));
- link.append(rubric);
- link.append(desc);
+ if (rubric.textContent) { link.append(rubric); }
+ if (desc.textContent) { link.append(desc); }
- el.nextElementSibling.nextElementSibling.classList.add('remove');
- el.nextElementSibling.classList.add('remove');
+ if (hasRubric && hasDesc) { el.nextElementSibling.nextElementSibling.classList.add('remove'); }
+ if (hasRubric || hasDesc) { el.nextElementSibling.classList.add('remove'); }
el.classList.add('remove');
elems.push(link);
- if (el.nextElementSibling.nextElementSibling.nextElementSibling) {
- checkForMoreCards(el.nextElementSibling.nextElementSibling.nextElementSibling, elems);
+ let checkAfterSibling = el.nextElementSibling;
+ if (hasRubric && hasDesc) {
+ checkAfterSibling = el.nextElementSibling.nextElementSibling.nextElementSibling;
+ } else if (hasRubric || hasDesc) {
+ checkAfterSibling = el.nextElementSibling.nextElementSibling;
+ }
+
+ if (checkAfterSibling) {
+ checkForMoreCards(checkAfterSibling, elems);
}
}
};
@@ -580,6 +606,7 @@ window.store = new (class {
},
carousel: () => 20,
loop: () => 30,
+ 'trending-banner': () => 6,
'series-cards': () => 100,
'tiger-cards': () => 35,
'tiger-vault-hero': () => 1,
@@ -589,7 +616,7 @@ window.store = new (class {
this.blockNames = Object.keys(this._blockQueryLimit);
this.spreadsheets = Object.keys(this._spreadsheets);
- this.initQueries();
+ // this.initQueries();
this._cache = {};
@@ -665,6 +692,7 @@ window.store = new (class {
*/
query(block) {
const id = getBlockId(block);
+ // this.initQueries();
let query = this.getQuery(block);
if (!query) {
@@ -884,3 +912,22 @@ export const generateArticleBlocker = (block, selector) => {
articleBody.appendChild(articleBlocker);
};
+
+/**
+ * Generates a Block for trending loop articles, assigns the slot article and appends the block.
+ *
+ * This function must be awaited!
+ *
+ * @param {block} Block that should contain the new trending-banner block.
+ * @param {slotName} Slot name that should be assigned to the new trending-banner block.
+ */
+export const createAndInsertTrendingBannerBlock = async (block, slotName) => {
+ // TODO use trending query instead of loop articles
+ const trendingBannerBlock = buildBlock('trending-banner', [[]]);
+ trendingBannerBlock.classList.add('loop-article');
+ trendingBannerBlock.setAttribute('slot', slotName);
+ block.append(trendingBannerBlock);
+ decorateBlock(trendingBannerBlock);
+ await loadBlock(trendingBannerBlock);
+ return trendingBannerBlock;
+};