diff --git a/express/blocks/browse-by-category/browse-by-category.css b/express/blocks/browse-by-category/browse-by-category.css
index ec8073ac9..af055f341 100644
--- a/express/blocks/browse-by-category/browse-by-category.css
+++ b/express/blocks/browse-by-category/browse-by-category.css
@@ -1,26 +1,27 @@
-main .browse-by-category-container {
- padding-bottom: 24px;
-}
-
-main .browse-by-category-container .browse-by-category {
+main .browse-by-category-wrapper.fullwidth {
+ margin: 0;
+ max-width: fit-content;
+ }
+
+ main .browse-by-category {
max-width: max-content;
-}
-
-main .browse-by-category-container .browse-by-category-wrapper {
- padding: 0;
- max-width: none;
-}
-
-main .browse-by-category-container .browse-by-category .carousel-container .carousel-platform {
- align-items: flex-start;
-}
-
-main .browse-by-category-container .browse-by-category .carousel-container .button.carousel-arrow {
+ }
+
+ main .browse-by-category .carousel-container .carousel-platform {
+ align-items: start;
+ }
+
+ main .browse-by-category.card .carousel-container .carousel-platform {
+ gap: 14px;
+ margin: 6px 0 0 0;
+ }
+
+ main .browse-by-category .carousel-container .button.carousel-arrow {
position: absolute;
top: 46px;
-}
-
-main .browse-by-category-container .browse-by-category-heading-section {
+ }
+
+ main .browse-by-category .browse-by-category-heading-section {
display: flex;
justify-content: space-between;
flex-direction: column;
@@ -29,28 +30,28 @@ main .browse-by-category-container .browse-by-category-heading-section {
margin-left: auto;
margin-right: auto;
margin-bottom: 10px;
-}
-
-main .browse-by-category-container .browse-by-category-heading-section .browse-by-category-heading {
+ }
+
+ main .browse-by-category .browse-by-category-heading-section .browse-by-category-heading {
text-align: left;
font-size: 28px;
line-height: 30px;
-}
-
-main .browse-by-category-container .browse-by-category-heading-section .browse-by-category-link-wrapper {
+ }
+
+ main .browse-by-category .browse-by-category-heading-section .browse-by-category-link-wrapper {
margin: 8px 0 0;
-}
-
-main .browse-by-category-container .browse-by-category-link {
+ }
+
+ main .browse-by-category .browse-by-category-link {
font-size: 16px;
line-height: 22px;
display: flex;
padding: 0;
white-space: nowrap;
width: max-content;
-}
-
-main .browse-by-category-container .browse-by-category-link::after {
+ }
+
+ main .browse-by-category .browse-by-category-link::after {
display: flex;
width: 6px;
height: 6px;
@@ -61,14 +62,14 @@ main .browse-by-category-container .browse-by-category-link::after {
border-style: solid;
border-color: var(--color-info-accent);
transform-origin: 75% 75%;
- transform: rotate( -45deg );
+ transform: rotate(-45deg);
content: "";
margin-top: 5px;
margin-left: 5px;
margin-right: 2.25px;
-}
-
-main .browse-by-category-container .browse-by-category-category {
+ }
+
+ main .browse-by-category:not(.card) .browse-by-category-card {
position: relative;
display: flex;
gap: 8px;
@@ -77,19 +78,29 @@ main .browse-by-category-container .browse-by-category-category {
min-width: 123px;
margin: 0;
padding: 10px 8px 0 8px;
-}
-
-main .browse-by-category-container .browse-by-category-category-anchor {
+ }
+
+ main .browse-by-category.card .browse-by-category-card {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ }
+
+ main .browse-by-category .browse-by-category-card-link {
position: absolute;
height: 100%;
width: 100%;
-}
-
-main .browse-by-category-container .browse-by-category-category-anchor:hover ~ .browse-by-category-category-image-wrapper img {
- transform: scale(1.1) matrix(1, -0.07, 0.07, 1, 0, 0);
-}
-
-main .browse-by-category-container .browse-by-category-category-image-wrapper {
+ }
+
+ main .browse-by-category:not(.card) .browse-by-category-card-link:hover ~ .browse-by-category-image-wrapper img {
+ transform: scale(1.1) matrix(1, -0.07, 0.05, 1, 0, 0);
+ }
+
+ main .browse-by-category.card .browse-by-category-card-link:hover ~ .browse-by-category-image-wrapper img {
+ transform: scale(1.1) matrix(1, -0.01, 0.01, 1, 0, 0);
+ }
+
+ main .browse-by-category .browse-by-category-image-wrapper {
background-color: var(--color-gray-200);
min-height: 90px;
width: 148px;
@@ -98,21 +109,21 @@ main .browse-by-category-container .browse-by-category-category-image-wrapper {
justify-content: center;
align-items: center;
pointer-events: none;
-}
-
-main .browse-by-category-container .browse-by-category-category-image-wrapper img {
+ }
+
+ main .browse-by-category:not(.card) .browse-by-category-image-wrapper img {
display: block;
object-fit: cover;
width: 80px;
height: 80px;
transform: matrix(1, -0.07, 0.07, 1, 0, 0);
- box-shadow: 0 0 6px #0000001F;
+ box-shadow: 0 0 6px #0000001f;
border-radius: 8px;
opacity: 1;
transition: transform 0.2s ease-in-out;
-}
-
-main .browse-by-category-container .browse-by-category-category-image-wrapper .browse-by-category-category-image-shadow {
+ }
+
+ main .browse-by-category .browse-by-category-image-wrapper .browse-by-category-image-shadow {
position: absolute;
width: 76px;
height: 76px;
@@ -120,31 +131,78 @@ main .browse-by-category-container .browse-by-category-category-image-wrapper .b
background: var(--color-gray-300) 0 0 no-repeat padding-box;
border-radius: 10px;
opacity: 1;
-}
-
-main .browse-by-category-container .browse-by-category-category-title {
+ }
+
+ main .browse-by-category.card .browse-by-category-image-shadow-wrapper {
+ display: block;
+ object-fit: cover;
+ transform: matrix(1, -0.07, 0.07, 1, 0, 0);
+ box-shadow: 0 0 6px #0000001f;
+ border-radius: 8px;
+ opacity: 1;
+ transition: transform 0.2s ease-in-out;
+ }
+
+ main .browse-by-category.card .browse-by-category-image-wrapper {
+ background-color: #f8f8f8;
+ min-height: 90px;
+ height: 116px;
+ width: 182px;
+ border-radius: 10px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ pointer-events: none;
+ }
+
+ main .browse-by-category.card .browse-by-category-image-wrapper img {
+ display: block;
+ object-fit: contain;
+ max-width: 110px;
+ max-height: 97px;
+ height: 100%;
+ transform: matrix(1, -0.01, 0.01, 1, 0, 0);
+ box-shadow: 0 0 6px #0000001f;
+ border-radius: 8px;
+ opacity: 1;
+ transition: transform 0.2s ease-in-out;
+ }
+
+ main .browse-by-category.card .browse-by-category-image-wrapper .browse-by-category-image-shadow {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ transform: matrix(0.90, -0.18, 0.20, 0.90, -14, 0);
+ background: #d6d6d6e5;
+ }
+
+ main .browse-by-category:not(.card) .browse-by-category-card-title {
margin: 0;
font-size: 18px;
line-height: 24px;
font-weight: 700;
max-width: 148px;
-}
-
-@media (min-width: 900px) {
- main .browse-by-category-container .browse-by-category-heading-section {
- flex-direction: row;
- padding: 0;
- }
-
- main .browse-by-category-container .browse-by-category-wrapper {
- padding: 0 28px;
- max-width: none;
- }
-}
-
-@media (min-width: 1200px) {
- main .browse-by-category-container .browse-by-category-wrapper {
- padding: 0 28px;
- max-width: none;
+ }
+
+ main .browse-by-category.card .browse-by-category-card-title {
+ margin: 6px 0;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 700;
+ align-self: start;
+ }
+
+ @media (min-width: 900px) {
+ main .section .browse-by-category-wrapper {
+ padding: 0 28px;
+ }
+
+ main .section .browse-by-category-wrapper {
+ max-width: none;
+ }
+
+ main .browse-by-category .browse-by-category-heading-section {
+ flex-direction: row;
+ padding: 0;
}
-}
+ }
diff --git a/express/blocks/browse-by-category/browse-by-category.js b/express/blocks/browse-by-category/browse-by-category.js
index 7ceec92c5..328b268ec 100644
--- a/express/blocks/browse-by-category/browse-by-category.js
+++ b/express/blocks/browse-by-category/browse-by-category.js
@@ -11,75 +11,94 @@
*/
/* eslint-disable import/named, import/extensions */
-import {
- createTag,
-} from '../../scripts/scripts.js';
+import { createTag } from '../../scripts/scripts.js';
-import {
- buildCarousel,
-} from '../shared/carousel.js';
+import { buildCarousel } from '../shared/carousel.js';
-export function decorateHeading($block, payload) {
- const $headingSection = createTag('div', { class: 'browse-by-category-heading-section' });
- const $heading = createTag('h3', { class: 'browse-by-category-heading' });
- const $viewAllButtonWrapper = createTag('p', { class: 'browse-by-category-link-wrapper' });
+export function decorateHeading(block, payload) {
+ const headingSection = createTag('div', { class: 'browse-by-category-heading-section' });
+ const heading = createTag('h3', { class: 'browse-by-category-heading' });
+ const viewAllButtonWrapper = createTag('p', { class: 'browse-by-category-link-wrapper' });
if (payload.viewAllLink.href !== '') {
- const $viewAllButton = createTag('a', { class: 'browse-by-category-link', href: payload.viewAllLink.href });
- $viewAllButton.textContent = payload.viewAllLink.text;
- $viewAllButtonWrapper.append($viewAllButton);
+ const viewAllButton = createTag('a', {
+ class: 'browse-by-category-link',
+ href: payload.viewAllLink.href,
+ });
+ viewAllButton.textContent = payload.viewAllLink.text;
+ viewAllButtonWrapper.append(viewAllButton);
}
- $heading.textContent = payload.heading;
- $headingSection.append($heading, $viewAllButtonWrapper);
- $block.append($headingSection);
+ heading.textContent = payload.heading;
+ headingSection.append(heading, viewAllButtonWrapper);
+ block.append(headingSection);
}
-export function decorateCategories($block, payload) {
- const $categoriesWrapper = createTag('div', { class: 'browse-by-category-categories-wrapper' });
-
- payload.categories.forEach((category) => {
- const $category = createTag('div', { class: 'browse-by-category-category' });
- const $categoryImageWrapper = createTag('div', { class: 'browse-by-category-category-image-wrapper' });
- const $categoryImageShadow = createTag('div', { class: 'browse-by-category-category-image-shadow' });
- const $categoryImage = category.$image;
- const $categoryTitle = createTag('h4', { class: 'browse-by-category-category-title' });
- const $categoryAnchor = createTag('a', { class: 'browse-by-category-category-anchor' });
-
- $categoryTitle.textContent = category.text;
- $categoryAnchor.href = category.link;
- $categoryImageWrapper.append($categoryImageShadow, $categoryImage);
- $category.append($categoryAnchor, $categoryImageWrapper, $categoryTitle);
- $categoriesWrapper.append($category);
+export function decorateCategories(block, payload) {
+ const categoriesWrapper = createTag('div', { class: 'browse-by-category-categories-wrapper' });
+
+ payload.categories.forEach((categoryCard) => {
+ const category = createTag('div', { class: 'browse-by-category-card' });
+ const categoryImageWrapper = createTag('div', { class: 'browse-by-category-image-wrapper' });
+ const categoryImageShadowWrapper = createTag('div', {
+ class: 'browse-by-category-image-shadow-wrapper',
+ });
+ const categoryImageShadow = createTag('div', { class: 'browse-by-category-image-shadow' });
+ const categoryImage = categoryCard.image;
+ const categoryTitle = createTag('h4', { class: 'browse-by-category-card-title' });
+ const categoryAnchor = createTag('a', { class: 'browse-by-category-card-link' });
+
+ categoryTitle.textContent = categoryCard.text;
+ categoryAnchor.href = categoryCard.link;
+ categoryImageShadowWrapper.append(categoryImageShadow, categoryImage);
+ categoryImageWrapper.append(categoryImageShadowWrapper);
+ category.append(categoryAnchor, categoryImageWrapper, categoryTitle);
+ categoriesWrapper.append(category);
});
- $block.append($categoriesWrapper);
+ block.append(categoriesWrapper);
}
-export default async function decorate($block) {
- const $rows = Array.from($block.children);
- const $headingDiv = $rows.shift();
+export default async function decorate(block) {
+ const rows = Array.from(block.children);
+ const headingDiv = rows.shift();
const payload = {
- heading: $headingDiv.querySelector('h4') ? $headingDiv.querySelector('h4').textContent.trim() : '',
+ heading: headingDiv.querySelector('h4')
+ ? headingDiv.querySelector('h4').textContent.trim()
+ : '',
viewAllLink: {
- text: $headingDiv.querySelector('a.button') ? $headingDiv.querySelector('a.button').textContent.trim() : '',
- href: $headingDiv.querySelector('a.button') ? $headingDiv.querySelector('a.button').href : '',
+ text: headingDiv.querySelector('a.button')
+ ? headingDiv.querySelector('a.button').textContent.trim()
+ : '',
+ href: headingDiv.querySelector('a.button') ? headingDiv.querySelector('a.button').href : '',
},
categories: [],
};
- $rows.forEach(($row) => {
+ rows.forEach((row) => {
payload.categories.push({
- $image: $row.querySelector('picture'),
- text: $row.querySelector('a.button') ? $row.querySelector('a.button').textContent.trim() : 'missing category text',
- link: $row.querySelector('a.button') ? $row.querySelector('a.button').href : 'missing category link',
+ image: row.querySelector('picture'),
+ text: row.querySelector('a.button')
+ ? row.querySelector('a.button').textContent.trim()
+ : 'missing category text',
+ link: row.querySelector('a.button')
+ ? row.querySelector('a.button').href
+ : 'missing category link',
});
});
- $block.innerHTML = '';
+ block.innerHTML = '';
- decorateHeading($block, payload);
- decorateCategories($block, payload);
- buildCarousel('.browse-by-category-category', $block, false);
+ decorateHeading(block, payload);
+ decorateCategories(block, payload);
+ buildCarousel('.browse-by-category-card', block, false);
+
+ if (block.classList.contains('fullwidth')) {
+ const blockWrapper = block.parentNode;
+
+ if (blockWrapper && blockWrapper.classList.contains('browse-by-category-wrapper')) {
+ blockWrapper.classList.add('fullwidth');
+ }
+ }
}
diff --git a/express/blocks/link-list/link-list.js b/express/blocks/link-list/link-list.js
index bc6ad1878..d9b5ad543 100644
--- a/express/blocks/link-list/link-list.js
+++ b/express/blocks/link-list/link-list.js
@@ -71,4 +71,8 @@ export default async function decorate($block) {
buildCarousel('p.button-container', div, false);
div.append(platformEl);
}
+
+ if (window.location.href.includes('/express/templates/')) {
+ import('../../scripts/ckg-link-list.js');
+ }
}
diff --git a/express/blocks/long-text/long-text.css b/express/blocks/long-text/long-text.css
new file mode 100644
index 000000000..66b19fbb2
--- /dev/null
+++ b/express/blocks/long-text/long-text.css
@@ -0,0 +1,32 @@
+/* to remove after wrapper deprecation */
+main .section div.long-text-wrapper {
+ max-width: none;
+ padding: 0;
+}
+
+main .long-text {
+ margin: 0 28px;
+ padding: 28px;
+ background-color: var(--color-gray-100);
+ border-radius: 20px;
+}
+
+main .long-text h2,
+main .long-text p {
+ text-align: left;
+}
+
+main .long-text h2 {
+ font-size: var(--heading-font-size-m);
+}
+
+main .long-text p {
+ font-size: var(--body-font-size-s);
+}
+
+@media (min-width: 900px) {
+ main .long-text {
+ margin: 0 60px;
+ padding: 28px;
+ }
+}
diff --git a/express/blocks/long-text/long-text.js b/express/blocks/long-text/long-text.js
new file mode 100644
index 000000000..7033ca8b0
--- /dev/null
+++ b/express/blocks/long-text/long-text.js
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+export default function decorate(block) {
+ if (block.textContent.trim() === '') {
+ if (block.parentElement.classList.contains('long-text-wrapper')) {
+ block.parentElement.remove();
+ } else {
+ block.remove();
+ }
+ }
+}
diff --git a/express/blocks/search-marquee/autocomplete-api-v3.js b/express/blocks/search-marquee/autocomplete-api-v3.js
new file mode 100644
index 000000000..7e31a9162
--- /dev/null
+++ b/express/blocks/search-marquee/autocomplete-api-v3.js
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+const url = 'https://adobesearch-atc.adobe.io/uss/v3/autocomplete';
+const experienceId = 'default-templates-autocomplete-v1';
+const scopeEntities = [
+ 'HzTemplate',
+ // 'HzTextLockup', 'Icon', 'Photo', 'DesignAsset', 'Background'
+];
+const emptyRes = { queryResults: [{ items: [] }] };
+export default async function fetchAPI({ limit = 5, textQuery, locale = 'en-US' }) {
+ if (!textQuery) {
+ return [];
+ }
+
+ const res = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'x-api-key': 'projectx_marketing_web',
+ 'content-type': 'application/json',
+ },
+ body: JSON.stringify({
+ experienceId,
+ textQuery,
+ locale,
+ queries: [
+ {
+ limit,
+ id: experienceId,
+ scope: {
+ entities: scopeEntities,
+ },
+ },
+ ],
+ }),
+ })
+ .then((response) => response.json())
+ .then((response) => (response.queryResults?.[0]?.items ? response : emptyRes))
+ .catch((err) => {
+ // eslint-disable-next-line no-console
+ console.error('Autocomplete API Error: ', err);
+ return emptyRes;
+ });
+ return res.queryResults[0].items;
+}
diff --git a/express/blocks/search-marquee/search-marquee.css b/express/blocks/search-marquee/search-marquee.css
new file mode 100644
index 000000000..174362156
--- /dev/null
+++ b/express/blocks/search-marquee/search-marquee.css
@@ -0,0 +1,206 @@
+main .section .search-marquee-wrapper {
+ max-width: none;
+}
+
+main .search-marquee {
+ padding: 32px 28px;
+ margin-bottom: 24px;
+ background-position: center;
+ background-size: cover;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+main .search-marquee .search-marquee-bg {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ z-index: 0;
+}
+
+main .search-marquee .search-dropdown-container {
+ background: #FFFFFF;
+ top: calc(100% + 6px);
+ border-radius: 12px;
+ box-shadow: 0 0 20px #00000029;
+ position: absolute;
+ width: 100%;
+ box-sizing: border-box;
+ left: 0;
+ z-index: 3;
+}
+
+main .search-marquee .search-dropdown-container .dropdown-title {
+ margin: 0;
+ font-size: 14px;
+ color: var(--color-gray-500);
+}
+
+main .search-marquee .search-dropdown-container .trends-container,
+main .search-marquee .search-dropdown-container .suggestions-container {
+ padding: 20px 28px 0;
+ text-align: left;
+}
+
+main .search-marquee .search-dropdown-container .trends-container .from-scratch-link {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 16px;
+}
+
+main .search-marquee .search-dropdown-container .trends-container ul,
+main .search-marquee .search-dropdown-container .suggestions-container ul {
+ padding-left: 0;
+ list-style: none;
+ max-height: 216px;
+ overflow: auto;
+}
+
+main .search-marquee .search-dropdown-container .trends-container .trends-wrapper li,
+main .search-marquee .search-dropdown-container .suggestions-container li {
+ margin-bottom: 16px;
+}
+
+main .search-marquee .search-dropdown-container .suggestions-container li {
+ cursor: pointer;
+}
+
+main .search-marquee .search-dropdown-container .suggestions-container li:hover {
+ color: var(--color-info-accent);
+}
+
+main .search-marquee .search-dropdown-container .trends-container .trends-wrapper li a {
+ color: var(--color-black);
+}
+
+main .search-marquee .search-dropdown-container .suggestions-container {
+ min-width: 300px;
+}
+
+main .search-marquee .search-dropdown-container .free-plans-container .free-plan-widget {
+ width: 100%;
+ border-radius: 0 0 12px 12px;
+ flex-direction: column;
+ align-items: center;
+ gap: 10px;
+ margin: 0;
+ justify-content: center;
+}
+
+main .search-marquee .search-bar-wrapper {
+ position: relative;
+ width: 100%;
+ max-width: 560px;
+ margin: 0 28px;
+ height: max-content;
+ transition: max-width 0.5s;
+}
+
+main .search-marquee .search-bar-wrapper .search-bar {
+ box-sizing: border-box;
+ margin: 0;
+ font-family: var(--body-font-family);
+ font-weight: 400;
+ font-size: 16px;
+ padding: 10px 40px;
+ border: var(--color-gray-200) 2px solid;
+ border-radius: 100px;
+ max-width: 560px;
+ width: 100%;
+ height: 41px;
+ transition: max-width 0.5s, padding-right 0.5s, padding-left 0.5s;
+}
+
+main .search-marquee .search-bar-wrapper .search-bar::placeholder {
+ transition: color 0.5s;
+}
+
+main .search-marquee .search-bar-wrapper .search-bar:focus-visible,
+main .search-marquee .search-bar-wrapper .search-bar:focus {
+ border: #444444 2px solid;
+ outline: none;
+}
+
+main .search-marquee .search-bar-wrapper .icon.icon-search {
+ position: absolute;
+ z-index: 1;
+ height: 16px;
+ width: 16px;
+ cursor: pointer;
+ top: 50%;
+ transform: translateY(-50%);
+ transition: left 0.5s;
+ left: 16px;
+}
+
+main .search-marquee .search-bar-wrapper .icon.icon-search-clear {
+ position: absolute;
+ z-index: 1;
+ height: 16px;
+ width: 16px;
+ cursor: pointer;
+ top: 50%;
+ transform: translateY(-50%);
+ right: 40px;
+}
+
+main .search-marquee .carousel-container {
+ margin-top: 32px;
+ margin-bottom: 0;
+}
+
+main .search-marquee.spreadsheet-powered .carousel-container {
+ visibility: hidden;
+}
+
+main .search-marquee.spreadsheet-powered .carousel-container.appear {
+ visibility: unset;
+}
+
+main .search-marquee .carousel-container .carousel-platform {
+ margin: auto;
+}
+
+main .search-marquee .carousel-container .carousel-platform p.button-container {
+ margin: 0;
+ border: none;
+}
+
+main .search-marquee .carousel-container .carousel-platform a {
+ display: block;
+ box-sizing: border-box;
+ min-width: 160px;
+ min-height: 48px;
+ padding: 14px 27px;
+ color: var(--color-black);
+ font-size: 16px;
+ line-height: 20px;
+ background: var(--color-white);
+ border-radius: 50px;
+ margin: 0 4px;
+ border: none;
+}
+
+main .search-marquee .carousel-container .carousel-platform a:hover {
+ background-color: var(--color-gray-200);
+}
+
+main .search-marquee .hidden {
+ display: none;
+}
+
+main .search-marquee .carousel-container .carousel-fader-right,
+main .search-marquee .carousel-container .carousel-fader-left {
+ background: unset;
+}
+
+@media (min-width: 600px) {
+ main .search-marquee .search-dropdown-container .free-plans-container .free-plan-widget {
+ flex-direction: row;
+ }
+}
diff --git a/express/blocks/search-marquee/search-marquee.js b/express/blocks/search-marquee/search-marquee.js
new file mode 100644
index 000000000..d5d6acb26
--- /dev/null
+++ b/express/blocks/search-marquee/search-marquee.js
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ buildStaticFreePlanWidget,
+ createTag,
+ fetchPlaceholders,
+ getIconElement,
+ getLocale,
+ getMetadata,
+} from '../../scripts/scripts.js';
+
+import { buildCarousel } from '../shared/carousel.js';
+import fetchAllTemplatesMetadata from '../../scripts/all-templates-metadata.js';
+
+function handlelize(str) {
+ return str.normalize('NFD')
+ .replace(/[\u0300-\u036f]/g, '') // Remove accents
+ .replace(/(\W+|\s+)/g, '-') // Replace space and other characters by hyphen
+ .replace(/--+/g, '-') // Replaces multiple hyphens by one hyphen
+ .replace(/(^-+|-+$)/g, '') // Remove extra hyphens from beginning or end of the string
+ .toLowerCase(); // To lowercase
+}
+
+function logSearch(form, url = 'https://main--express-website--adobe.hlx.page/express/search-terms-log') {
+ if (form) {
+ const input = form.querySelector('input');
+ fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ data: {
+ keyword: input.value,
+ locale: getLocale(window.location),
+ timestamp: Date.now(),
+ audience: document.body.dataset.device,
+ },
+ }),
+ });
+ }
+}
+
+function initSearchFunction(block) {
+ const searchBarWrapper = block.querySelector('.search-bar-wrapper');
+
+ const searchDropdown = searchBarWrapper.querySelector('.search-dropdown-container');
+ const searchForm = searchBarWrapper.querySelector('.search-form');
+ const searchBar = searchBarWrapper.querySelector('input.search-bar');
+ const clearBtn = searchBarWrapper.querySelector('.icon-search-clear');
+ const trendsContainer = searchBarWrapper.querySelector('.trends-container');
+ const suggestionsContainer = searchBarWrapper.querySelector('.suggestions-container');
+ const suggestionsList = searchBarWrapper.querySelector('.suggestions-list');
+
+ clearBtn.style.display = 'none';
+
+ searchBar.addEventListener('click', (e) => {
+ e.stopPropagation();
+ searchDropdown.classList.remove('hidden');
+ }, { passive: true });
+
+ searchBar.addEventListener('keyup', () => {
+ if (searchBar.value !== '') {
+ clearBtn.style.display = 'inline-block';
+ trendsContainer.classList.add('hidden');
+ suggestionsContainer.classList.remove('hidden');
+ } else {
+ clearBtn.style.display = 'none';
+ trendsContainer.classList.remove('hidden');
+ suggestionsContainer.classList.add('hidden');
+ }
+ }, { passive: true });
+
+ document.addEventListener('click', (e) => {
+ const { target } = e;
+ if (target !== searchBarWrapper && !searchBarWrapper.contains(target)) {
+ searchDropdown.classList.add('hidden');
+ }
+ }, { passive: true });
+
+ const redirectSearch = async () => {
+ const placeholders = await fetchPlaceholders();
+ const taskMap = JSON.parse(placeholders['task-name-mapping']);
+
+ const format = getMetadata('placeholder-format');
+ let currentTasks = '';
+ let searchInput = searchBar.value.toLowerCase() || getMetadata('topics');
+
+ const tasksFoundInInput = Object.entries(taskMap).filter((task) => task[1].some((word) => {
+ const searchValue = searchBar.value.toLowerCase();
+ return searchValue.indexOf(word.toLowerCase()) >= 0;
+ })).sort((a, b) => b[0].length - a[0].length);
+
+ if (tasksFoundInInput.length > 0) {
+ tasksFoundInInput[0][1].sort((a, b) => b.length - a.length).forEach((word) => {
+ searchInput = searchInput.toLowerCase().replace(word.toLowerCase(), '');
+ });
+
+ searchInput = searchInput.trim();
+ [[currentTasks]] = tasksFoundInInput;
+ }
+
+ const locale = getLocale(window.location);
+ const urlPrefix = locale === 'us' ? '' : `/${locale}`;
+ const topicUrl = searchInput ? `/${searchInput}` : '';
+ const taskUrl = `/${handlelize(currentTasks.toLowerCase())}`;
+ const targetPath = `${urlPrefix}/express/templates${taskUrl}${topicUrl}`;
+ const allTemplatesMetadata = await fetchAllTemplatesMetadata();
+ const pathMatch = (e) => e.url === targetPath;
+ if (allTemplatesMetadata.some(pathMatch)) {
+ window.location = `${window.location.origin}${targetPath}`;
+ } else {
+ const searchUrlTemplate = `/express/templates/search?tasks=${currentTasks}&phformat=${format}&topics=${searchInput || "''"}&q=${searchInput || "''"}`;
+ window.location = `${window.location.origin}${urlPrefix}${searchUrlTemplate}`;
+ }
+ };
+
+ searchForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ searchBar.disabled = true;
+ logSearch(e.currentTarget);
+ await redirectSearch();
+ });
+
+ clearBtn.addEventListener('click', () => {
+ searchBar.value = '';
+ suggestionsList.innerHTML = '';
+ trendsContainer.classList.remove('hidden');
+ suggestionsContainer.classList.add('hidden');
+ clearBtn.style.display = 'none';
+ }, { passive: true });
+
+ const suggestionsListUIUpdateCB = (suggestions) => {
+ suggestionsList.innerHTML = '';
+ const searchBarVal = searchBar.value.toLowerCase();
+ if (suggestions && !(suggestions.length <= 1 && suggestions[0]?.query === searchBarVal)) {
+ suggestions.forEach((item) => {
+ const li = createTag('li', { tabindex: 0 });
+ const valRegEx = new RegExp(searchBar.value, 'i');
+ li.innerHTML = item.query.replace(valRegEx, `${searchBarVal}`);
+ li.addEventListener('click', () => {
+ if (item.query === searchBar.value) return;
+ searchBar.value = item.query;
+ searchBar.dispatchEvent(new Event('input'));
+ });
+
+ suggestionsList.append(li);
+ });
+ }
+ };
+
+ import('./useInputAutocomplete.js').then(({ default: useInputAutocomplete }) => {
+ const { inputHandler } = useInputAutocomplete(
+ suggestionsListUIUpdateCB, { throttleDelay: 300, debounceDelay: 500, limit: 7 },
+ );
+ searchBar.addEventListener('input', inputHandler);
+ });
+}
+
+async function decorateSearchFunctions(block) {
+ const placeholders = await fetchPlaceholders();
+ const searchBarWrapper = createTag('div', { class: 'search-bar-wrapper' });
+ const searchForm = createTag('form', { class: 'search-form' });
+ const searchBar = createTag('input', {
+ class: 'search-bar',
+ type: 'text',
+ placeholder: placeholders['template-search-placeholder'] ?? 'Search for over 50,000 templates',
+ enterKeyHint: placeholders.search ?? 'Search',
+ });
+
+ // Tasks Dropdown
+
+ searchForm.append(searchBar);
+ const searchIcon = getIconElement('search');
+ searchIcon.loading = 'lazy';
+ const searchClearIcon = getIconElement('search-clear');
+ searchClearIcon.loading = 'lazy';
+ searchBarWrapper.append(searchIcon, searchClearIcon);
+ searchBarWrapper.append(searchForm);
+
+ block.append(searchBarWrapper);
+}
+
+// function downloadBackgroundImg(block) {}
+
+function decorateBackground(block) {
+ const supportedImgFormat = ['jpeg', 'jpg', 'webp', 'png', 'svg'];
+ const supportedVideoFormat = ['mp4'];
+ const mediaRow = block.querySelector('div:nth-child(2)');
+
+ if (mediaRow) {
+ const mediaEl = mediaRow.querySelector('a, :scope > div');
+ if (mediaEl) {
+ const media = mediaEl.href || mediaEl.textContent;
+ const splitArr = media.split('.');
+
+ if (supportedImgFormat.includes(splitArr[splitArr.length - 1])) {
+ const dummyImg = createTag('img');
+ dummyImg.src = media;
+ dummyImg.style.display = 'none';
+ block.append(dummyImg);
+ block.style.backgroundImage = `url(${media})`;
+ }
+
+ if (supportedVideoFormat.includes(splitArr[splitArr.length - 1])) {
+ // todo: support video background too
+ }
+ }
+
+ mediaRow.remove();
+ }
+}
+
+async function buildSearchDropdown(block) {
+ const placeholders = await fetchPlaceholders();
+
+ const searchBarWrapper = block.querySelector('.search-bar-wrapper');
+ if (searchBarWrapper) {
+ const dropdownContainer = createTag('div', { class: 'search-dropdown-container hidden' });
+ const trendsContainer = createTag('div', { class: 'trends-container' });
+ const suggestionsContainer = createTag('div', { class: 'suggestions-container hidden' });
+ const suggestionsTitle = createTag('p', { class: 'dropdown-title' });
+ const suggestionsList = createTag('ul', { class: 'suggestions-list' });
+ const freePlanContainer = createTag('div', { class: 'free-plans-container' });
+
+ const fromScratchLink = block.querySelector('a');
+ const trendsTitle = placeholders['search-trends-title'];
+ let trends;
+ if (placeholders['search-trends']) trends = JSON.parse(placeholders['search-trends']);
+
+ if (fromScratchLink) {
+ const linkDiv = fromScratchLink.parentElement.parentElement;
+ const templateFreeAccentIcon = getIconElement('template-free-accent');
+ templateFreeAccentIcon.loading = 'lazy';
+ const arrowRightIcon = getIconElement('arrow-right');
+ arrowRightIcon.loading = 'lazy';
+ fromScratchLink.prepend(templateFreeAccentIcon);
+ fromScratchLink.append(arrowRightIcon);
+ fromScratchLink.classList.remove('button');
+ fromScratchLink.classList.add('from-scratch-link');
+ fromScratchLink.innerHTML.replaceAll('https://www.adobe.com/express/templates/default-marquee-from-scratch-link', getMetadata('search-marquee-from-scratch-link') || '/');
+ trendsContainer.append(fromScratchLink);
+ linkDiv.remove();
+ }
+
+ if (trendsTitle) {
+ const trendsTitleEl = createTag('p', { class: 'dropdown-title' });
+ trendsTitleEl.textContent = trendsTitle;
+ trendsContainer.append(trendsTitleEl);
+ }
+
+ if (trends) {
+ const trendsWrapper = createTag('ul', { class: 'trends-wrapper' });
+ for (const [key, value] of Object.entries(trends)) {
+ const trendLinkWrapper = createTag('li');
+ const trendLink = createTag('a', { class: 'trend-link', href: value });
+ trendLink.textContent = key;
+ trendLinkWrapper.append(trendLink);
+ trendsWrapper.append(trendLinkWrapper);
+ }
+ trendsContainer.append(trendsWrapper);
+ }
+
+ suggestionsTitle.textContent = placeholders['search-suggestions-title'];
+ suggestionsContainer.append(suggestionsTitle, suggestionsList);
+
+ const freePlanTags = await buildStaticFreePlanWidget();
+
+ freePlanContainer.append(freePlanTags);
+ dropdownContainer.append(trendsContainer, suggestionsContainer, freePlanContainer);
+ searchBarWrapper.append(dropdownContainer);
+ }
+}
+
+function decorateLinkList(block) {
+ const carouselItemsWrapper = block.querySelector(':scope > div:nth-of-type(2)');
+ if (carouselItemsWrapper) {
+ const showLinkList = getMetadata('show-search-marquee-link-list');
+ if ((showLinkList && !['yes', 'true', 'on', 'Y'].includes(showLinkList))
+ // no link list for templates root page
+ || window.location.pathname.endsWith('/express/templates/')
+ || window.location.pathname.endsWith('/express/templates')) {
+ carouselItemsWrapper.remove();
+ } else {
+ buildCarousel(':scope > div > p', carouselItemsWrapper);
+ const carousel = carouselItemsWrapper.querySelector('.carousel-container');
+ block.append(carousel);
+ }
+ }
+}
+
+export default async function decorate(block) {
+ // desktop-only block
+ if (document.body.dataset?.device !== 'desktop') {
+ block.remove();
+ return;
+ }
+ decorateBackground(block);
+ await decorateSearchFunctions(block);
+ await buildSearchDropdown(block);
+ initSearchFunction(block);
+ decorateLinkList(block);
+
+ const blockLinks = block.querySelectorAll('a');
+ if (blockLinks && blockLinks.length > 0) {
+ const linksPopulated = new CustomEvent('linkspopulated', { detail: blockLinks });
+ document.dispatchEvent(linksPopulated);
+ }
+ if (window.location.href.includes('/express/templates/')) {
+ import('../../scripts/ckg-link-list.js');
+ }
+}
diff --git a/express/blocks/search-marquee/useInputAutocomplete.js b/express/blocks/search-marquee/useInputAutocomplete.js
new file mode 100644
index 000000000..d49525a05
--- /dev/null
+++ b/express/blocks/search-marquee/useInputAutocomplete.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import { getLocale, getLanguage } from '../../scripts/scripts.js';
+import { memoize, throttle, debounce } from '../../scripts/utils.js';
+import fetchAPI from './autocomplete-api-v3.js';
+
+const memoizedFetchAPI = memoize(fetchAPI, {
+ key: (options) => options.textQuery,
+ ttl: 30 * 1000,
+});
+
+export default function useInputAutocomplete(
+ updateUIWithSuggestions,
+ { throttleDelay = 300, debounceDelay = 500, limit = 5 } = {},
+) {
+ const state = { query: '', waitingFor: '' };
+
+ const fetchAndUpdateUI = async () => {
+ const currentSearch = state.query;
+ state.waitingFor = currentSearch;
+ const suggestions = await memoizedFetchAPI({
+ textQuery: currentSearch,
+ limit,
+ locale: getLanguage(getLocale(window.location)),
+ });
+ if (state.waitingFor === currentSearch) {
+ updateUIWithSuggestions(suggestions);
+ }
+ };
+
+ const throttledFetchAndUpdateUI = throttle(fetchAndUpdateUI, throttleDelay, { trailing: true });
+ const debouncedFetchAndUpdateUI = debounce(fetchAndUpdateUI, debounceDelay);
+
+ const inputHandler = (e) => {
+ state.query = e.target.value;
+ if (state.query.length < 4 || state.query.endsWith(' ')) {
+ throttledFetchAndUpdateUI();
+ } else {
+ debouncedFetchAndUpdateUI();
+ }
+ };
+ return { inputHandler };
+}
diff --git a/express/blocks/template-list-ace/feedback-modal.js b/express/blocks/template-list-ace/feedback-modal.js
index bb43b46d4..9bb37c551 100644
--- a/express/blocks/template-list-ace/feedback-modal.js
+++ b/express/blocks/template-list-ace/feedback-modal.js
@@ -141,6 +141,7 @@ export function renderFeedbackModal(feedbackModalContent, result, feedbackState)
const { feedbackState: { showSubmittedTooltip } = {} } = BlockMediator.get('ace-state');
if (showSubmittedTooltip) showSubmittedTooltip();
} catch (err) {
+ // eslint-disable-next-line no-console
console.error(err);
}
feedbackModalContent.parentElement.parentElement.dispatchEvent(
diff --git a/express/blocks/template-list-ace/results-modal.js b/express/blocks/template-list-ace/results-modal.js
index d236fa2ba..89b9196b9 100644
--- a/express/blocks/template-list-ace/results-modal.js
+++ b/express/blocks/template-list-ace/results-modal.js
@@ -45,6 +45,7 @@ function getVoteHandler(result, category, feedbackState) {
feedbackState.category = category;
feedbackState.showThankyou();
} catch (err) {
+ // eslint-disable-next-line no-console
console.error(err);
}
};
@@ -365,6 +366,7 @@ export async function fetchResults(modalContent) {
(searchPositionMap.get(query) + NUM_RESULTS) % (RESULTS_ROTATION * NUM_RESULTS));
}
} catch (e) {
+ // eslint-disable-next-line no-console
console.error(e);
fetchingState.results = 'error';
} finally {
diff --git a/express/blocks/template-list/breadcrumbs.js b/express/blocks/template-list/breadcrumbs.js
new file mode 100644
index 000000000..ae9da74de
--- /dev/null
+++ b/express/blocks/template-list/breadcrumbs.js
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+import {
+ fetchPlaceholders,
+ getMetadata,
+ titleCase,
+ createTag,
+} from '../../scripts/scripts.js';
+import fetchAllTemplatesMetadata from '../../scripts/all-templates-metadata.js';
+
+function sanitize(str) {
+ return str.replaceAll(/[$@%'"]/g, '');
+}
+
+function getCrumbsForSearch(templatesUrl, allTemplatesMetadata, taskCategories) {
+ const { search, origin } = window.location;
+ let { tasks, topics } = new Proxy(new URLSearchParams(search), {
+ get: (searchParams, prop) => searchParams.get(prop),
+ });
+ tasks = sanitize(tasks);
+ topics = sanitize(topics);
+ const crumbs = [];
+ if (!tasks && !topics) {
+ return crumbs;
+ }
+ const shortTitle = getMetadata('short-title');
+ if (!shortTitle) {
+ return crumbs;
+ }
+
+ const lastCrumb = createTag('li');
+ lastCrumb.textContent = shortTitle;
+ crumbs.push(lastCrumb);
+ if (!tasks || !topics) {
+ return crumbs;
+ }
+
+ const taskUrl = `${templatesUrl}${tasks}`;
+ const foundTaskPage = allTemplatesMetadata
+ .some((t) => t.url === taskUrl.replace(origin, ''));
+
+ if (foundTaskPage) {
+ const taskCrumb = createTag('li');
+ const taskAnchor = createTag('a', { href: taskUrl });
+ taskCrumb.append(taskAnchor);
+ const translatedTasks = Object.entries(taskCategories)
+ .find(([_, t]) => t === tasks || t === tasks.replace(/-/g, ' '))
+ ?.[0]?.toLowerCase() ?? tasks;
+ taskAnchor.textContent = titleCase(translatedTasks);
+ crumbs.unshift(taskCrumb);
+ }
+
+ return crumbs;
+}
+
+function getCrumbsForSEOPage(templatesUrl, allTemplatesMetadata, taskCategories, segments) {
+ const { origin, pathname } = window.location;
+ const tasks = getMetadata('tasks')
+ // TODO: remove templateTasks and allTemplatesMetadata here after all content are updated
+ ?? getMetadata('templateTasks')
+ ?? allTemplatesMetadata[pathname]?.tasks
+ ?? allTemplatesMetadata[pathname]?.templateTasks;
+ const translatedTasks = Object.entries(taskCategories)
+ .find(([_, t]) => t === tasks || t === tasks.replace(/-/g, ' '))
+ ?.[0]?.toLowerCase() ?? tasks;
+ // we might have an inconsistent trailing slash problem
+ let builtUrl = templatesUrl.replace('templates/', 'templates');
+ const crumbs = [];
+ segments
+ .slice(0, segments.length - 1)
+ .forEach((currSeg) => {
+ const seg = sanitize(currSeg);
+ if (!seg) return;
+ builtUrl = `${builtUrl}/${seg}`;
+ // at least translate tasks seg
+ const translatedSeg = seg === tasks ? translatedTasks : seg;
+ const segmentCrumb = createTag('li');
+ if (allTemplatesMetadata.some((t) => t.url === builtUrl.replace(origin, ''))) {
+ const segmentLink = createTag('a', { href: builtUrl });
+ segmentLink.textContent = titleCase(translatedSeg);
+ segmentCrumb.append(segmentLink);
+ } else {
+ segmentCrumb.textContent = titleCase(translatedSeg);
+ }
+ crumbs.push(segmentCrumb);
+ });
+ const lastCrumb = createTag('li');
+ lastCrumb.textContent = getMetadata('short-title');
+ crumbs.push(lastCrumb);
+ return crumbs;
+}
+
+// returns null if no breadcrumbs
+// returns breadcrumbs as an li element
+export default async function getBreadcrumbs() {
+ // for backward compatibility
+ // TODO: remove this check after all content are updated
+ if (getMetadata('sheet-powered') !== 'Y' || !document.querySelector('.search-marquee')) {
+ return null;
+ }
+ const { origin, pathname } = window.location;
+ const regex = /(.*?\/express\/)templates(.*)/;
+ const matches = pathname.match(regex);
+ if (!matches) {
+ return null;
+ }
+ const placeholders = await fetchPlaceholders();
+ const [, homePath, children] = matches;
+ const breadcrumbs = createTag('ol', { class: 'templates-breadcrumbs' });
+
+ const homeCrumb = createTag('li');
+ const homeUrl = `${origin}${homePath}`;
+ const homeAnchor = createTag('a', { href: homeUrl });
+ homeAnchor.textContent = titleCase(placeholders.express || '') || 'Home';
+ homeCrumb.append(homeAnchor);
+ breadcrumbs.append(homeCrumb);
+
+ const templatesCrumb = createTag('li');
+ const templatesUrl = `${homeUrl}templates/`;
+ const templatesAnchor = createTag('a', { href: templatesUrl });
+ templatesAnchor.textContent = titleCase(placeholders.templates || '') || 'Templates';
+ templatesCrumb.append(templatesAnchor);
+ breadcrumbs.append(templatesCrumb);
+
+ const nav = createTag('nav', { 'aria-label': 'Breadcrumb' });
+ nav.append(breadcrumbs);
+
+ if (!children || children === '/') {
+ return nav;
+ }
+ const taskCategories = JSON.parse(placeholders['task-categories']);
+ const allTemplatesMetadata = await fetchAllTemplatesMetadata();
+ const isSearchPage = children.startsWith('/search?') || getMetadata('template-search-page') === 'Y';
+ const crumbs = isSearchPage
+ ? getCrumbsForSearch(templatesUrl, allTemplatesMetadata, taskCategories)
+ : getCrumbsForSEOPage(templatesUrl, allTemplatesMetadata, taskCategories, children.split('/'));
+
+ crumbs.forEach((c) => {
+ breadcrumbs.append(c);
+ });
+ return nav;
+}
diff --git a/express/blocks/template-list/template-list.css b/express/blocks/template-list/template-list.css
index 771017bb2..27b74b33d 100644
--- a/express/blocks/template-list/template-list.css
+++ b/express/blocks/template-list/template-list.css
@@ -1,4 +1,5 @@
-main .template-list-horizontal-fullwidth-container {
+main .template-list-horizontal-fullwidth-container,
+main .template-list-fullwidth-apipowered-container {
padding-top: 0;
}
@@ -76,24 +77,7 @@ main .template-list.fullwidth.lg-view {
main .template-list-wrapper.with-categories-list .template-list.fullwidth {
width: 100%;
-}
-
-@media (min-width: 600px) {
- main .template-list-wrapper.with-categories-list .template-list.fullwidth {
- width: calc(100% - 172px);
- }
-
- main .template-list-wrapper.with-categories-list .template-list.fullwidth.sm-view {
- max-width: calc(100% - 172px);
- }
-
- main .template-list-wrapper.with-categories-list .template-list.fullwidth.md-view {
- max-width: calc(100% - 260px);
- }
-
- main .template-list-wrapper.with-categories-list .template-list.fullwidth.lg-view {
- max-width: calc(100% - 337px);
- }
+ min-height: 600px;
}
main .template-list.sixcols > .masonry-col,
@@ -102,7 +86,7 @@ main .template-list.fullwidth > .masonry-col {
}
main .template-list.fullwidth.sm-view > .masonry-col {
- max-width: 165px;
+ max-width: 170px;
text-align: center;
}
@@ -501,26 +485,6 @@ main .template-list.fullwidth.sm-view .template.placeholder {
width: 106.33px;
}
-@media (min-width: 600px) {
- main .template-list.fullwidth.lg-view .template.placeholder {
- width: 352px;
- }
-
- main .template-list.fullwidth.md-view .template.placeholder {
- width: 227.33px;
- }
-
- main .template-list.fullwidth.sm-view .template.placeholder {
- width: 165px;
- }
-}
-
-@media (min-width: 900px) {
- main .template-list.fullwidth.md-view .template.placeholder {
- width: 258.5px;
- }
-}
-
main .template-list .template.placeholder div:nth-of-type(2) {
margin: 0;
position: absolute;
@@ -657,139 +621,7 @@ main .template-list.fullwidth.sm-view > .masonry-col {
text-align: center;
}
-@media (min-width: 600px) {
- main .template-list.fullwidth.lg-view > .masonry-col {
- max-width: 352px;
- text-align: center;
- }
-
- main .template-list.fullwidth.md-view > .masonry-col {
- max-width: 227.33px;
- text-align: center;
- }
-
- main .template-list.fullwidth.sm-view > .masonry-col {
- max-width: 165px;
- text-align: center;
- }
-
- main .template-list.fullwidth.lg-view .template.placeholder,
- main .template-list.fullwidth.md-view .template.placeholder,
- main .template-list.fullwidth.sm-view .template.placeholder {
- max-height: unset;
- }
-
- main .template-list.sm-view .template.placeholder > div:first-of-type {
- height: 95%;
- }
-}
-
-@media (min-width: 900px) {
- main .template-list-container > div,
- main .template-list-fourcols-container > div,
- main .template-list-horizontal-container > div {
- max-width: 900px;
- }
-
- main .template-list-sixcols-container > div {
- max-width: 748px;
- }
-
- main .template-list-fullwidth-container > div,
- main .template-list-fullwidth-container > .default-content-wrapper,
- main .template-list-fullwidth-apipowered-container > div,
- main .template-list-fullwidth-apipowered-container > .default-content-wrapper {
- max-width: none;
- padding: 0 28px;
- }
-
- main .template-list.sixcols > .masonry-col,
- main .template-list.fullwidth > .masonry-col {
- max-width: 187px;
- }
-
- main .template-list .template-title > div {
- flex-direction: row;
- justify-content: space-between;
- }
-
- main .template-list .template-title > div > * {
- text-align: left;
- }
-
- main .template-list.sixcols > .masonry-col,
- main .template-list.fullwidth > .masonry-col {
- max-width: 187px;
- }
-
- main .template-list.fullwidth.md-view > .masonry-col {
- max-width: 258.5px;
- text-align: center;
- }
-
- main .template-list .template {
- width: 240px;
- margin-left: 20px;
- margin-right: 20px;
- }
-
- main .template-list.sixcols .template:not(.placeholder),
- main .template-list.fullwidth .template:not(.placeholder) {
- margin: 11px 11px 9px 11px; /* make up for 4px height overlap in first child div */
- }
-
- main .template-list.sixcols .template.placeholder,
- main .template-list.fullwidth .template.placeholder {
- width: 145px;
- margin: 21px;
- }
-
- /* template-list inside columns */
-
- main .columns .template-list {
- margin-top: 0;
- margin-left: 40px;
- }
-
- main .template-list .template:not(.placeholder) .icon-free-badge {
- display: none;
- }
-}
-
-@media (min-width: 1200px) {
- main .template-list-fourcols-container > div {
- max-width: 1200px;
- }
-
- main .template-list-sixcols-container > div {
- max-width: 1122px;
- }
-
- main .template-list-fullwidth-container > div,
- main .template-list-fullwidth-apipowered-container > div {
- max-width: none;
- }
-
- /* template-list inside columns */
-
- main .columns .template-list {
- flex-direction: row;
- flex-wrap: nowrap;
- justify-content: flex-start;
- }
-
- main .columns .template-list .template {
- justify-content: flex-start;
- min-width: 132px;
- }
-
- main .columns .template-list .template-link {
- font-size: 0.875rem;
- font-weight: 400;
- }
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper {
+main .section .template-list-search-bar-wrapper {
position: relative;
max-width: 560px;
padding: 0 28px;
@@ -798,7 +630,11 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper {
transition: max-width 0.5s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-bar {
+main .template-list-fullwidth-apipowered-container.search-marquee-spreadsheet-powered-container .template-list-search-bar-wrapper {
+ margin-top: 0;
+}
+
+main .section .template-list-search-bar-wrapper .search-bar {
box-sizing: border-box;
margin: 0;
font-family: var(--body-font-family);
@@ -813,17 +649,17 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-b
transition: max-width 0.5s, padding-right 0.5s, padding-left 0.5s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-bar::placeholder {
+main .section .template-list-search-bar-wrapper .search-bar::placeholder {
transition: color 0.5s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-bar:focus-visible,
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-bar:focus {
+main .section .template-list-search-bar-wrapper .search-bar:focus-visible,
+main .section .template-list-search-bar-wrapper .search-bar:focus {
border: #444444 2px solid;
outline: none;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .icon.icon-search {
+main .section .template-list-search-bar-wrapper .icon.icon-search {
position: absolute;
z-index: 1;
height: 16px;
@@ -835,7 +671,7 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .icon.ico
left: 160px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .icon.icon-search-clear {
+main .section .template-list-search-bar-wrapper .icon.icon-search-clear {
position: absolute;
z-index: 1;
height: 16px;
@@ -846,11 +682,11 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .icon.ico
right: 40px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list .icon {
+main .section .template-list-search-bar-wrapper .task-dropdown-list .icon {
margin-right: 4px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown {
+main .section .template-list-search-bar-wrapper .search-dropdown {
position: absolute;
background: var(--color-white);
box-sizing: border-box;
@@ -863,15 +699,15 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-d
box-shadow: 0 0 16px #00000015;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.sticky-search-bar .search-dropdown {
+main .section .template-list-search-bar-wrapper.sticky-search-bar .search-dropdown {
max-width: unset;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown.hidden {
+main .section .template-list-search-bar-wrapper .search-dropdown.hidden {
display: none;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .icon {
+main .section .template-list-search-bar-wrapper .search-dropdown .icon {
position: unset;
z-index: unset;
height: 22px;
@@ -880,19 +716,19 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-d
transform: unset;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-heading-wrapper {
+main .section .template-list-search-bar-wrapper .search-dropdown .search-dropdown-heading-wrapper {
display: flex;
justify-content: space-between;
font-size: 14px;
line-height: 22px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-heading {
+main .section .template-list-search-bar-wrapper .search-dropdown .search-dropdown-heading {
color: var(--color-gray-500);
display: none;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-scratch {
+main .section .template-list-search-bar-wrapper .search-dropdown .search-dropdown-scratch {
display: flex;
align-items: center;
gap: 4px;
@@ -900,51 +736,7 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-d
font-weight: 700;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-scratch .icon.icon-flyer-icon-22 {
- filter: invert(21%) sepia(34%) saturate(36) hue-rotate(-136deg) brightness(119%) contrast(85%);
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-scratch .icon.icon-arrow-right {
- width: 16px;
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .free-plan-widget {
- margin-top: 16px;
- position: relative;
- background-color: var(--color-gray-100);
- border-radius: 20px;
- padding: 16px 24px;
- display: flex;
- gap: 16px;
- justify-content: center;
- font-size: var(--body-font-size-s);
- white-space: nowrap;
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .free-plan-widget > div {
- display: flex;
- align-items: center;
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .free-plan-widget > div > div:first-child {
- position: relative;
- margin-right: 6px;
- width: 14px;
- height: 14px;
- background: #33AB84;
- border-radius: 50%;
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .free-plan-widget > div > div:first-child img {
- position: absolute;
- top: 3px;
- left: 3px;
- fill: var(--color-white);
- width: 8px;
- max-height: 8px;
-}
-
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-container {
+main .section .template-list-search-bar-wrapper .task-dropdown-container {
position: absolute;
max-width: 120px;
margin: 0;
@@ -952,20 +744,20 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
transition: max-width 0.5s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.sticky-search-bar .task-dropdown-container {
+main .section .template-list-search-bar-wrapper.sticky-search-bar .task-dropdown-container {
z-index: 2;
overflow: hidden;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.collapsed .task-dropdown-container {
+main .section .template-list-search-bar-wrapper.collapsed .task-dropdown-container {
max-width: 0;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.ready .task-dropdown-container {
+main .section .template-list-search-bar-wrapper.ready .task-dropdown-container {
overflow: unset;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown {
+main .section .template-list-search-bar-wrapper .task-dropdown {
position: relative;
margin: 2px;
background-color: var(--color-gray-200);
@@ -975,7 +767,7 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
height: 37px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown .icon.icon-drop-down-arrow {
+main .section .template-list-search-bar-wrapper .task-dropdown .icon.icon-drop-down-arrow {
height: 10px;
width: 10px;
position: absolute;
@@ -985,11 +777,11 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
pointer-events: none;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown.active .icon.icon-drop-down-arrow {
+main .section .template-list-search-bar-wrapper .task-dropdown.active .icon.icon-drop-down-arrow {
transform: rotate(180deg);
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-toggle {
+main .section .template-list-search-bar-wrapper .task-dropdown-toggle {
font-family: var(--body-font-family);
color: #242424;
font-size: 14px;
@@ -1008,7 +800,7 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
transition: background-color 0.2s, border 0.2s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-toggle:after {
+main .section .template-list-search-bar-wrapper .task-dropdown-toggle:after {
content: '';
position: absolute;
right: 0;
@@ -1018,11 +810,11 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
height: calc(100% - 8px);
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-toggle:hover {
+main .section .template-list-search-bar-wrapper .task-dropdown-toggle:hover {
background-color: var(--color-gray-200);
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list {
+main .section .template-list-search-bar-wrapper .task-dropdown-list {
position: absolute;
display: flex;
width: 240px;
@@ -1042,28 +834,28 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
transition: max-height 0.2s, padding 0.2s;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list::-webkit-scrollbar {
+main .section .template-list-search-bar-wrapper .task-dropdown-list::-webkit-scrollbar {
width: 12px;
}
/* Handle */
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list::-webkit-scrollbar-thumb {
+main .section .template-list-search-bar-wrapper .task-dropdown-list::-webkit-scrollbar-thumb {
background: var(--color-gray-200);
border: 2px var(--color-white) solid;
border-radius: 12px;
}
/* Handle on hover */
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list::-webkit-scrollbar-thumb:hover {
+main .section .template-list-search-bar-wrapper .task-dropdown-list::-webkit-scrollbar-thumb:hover {
background: var(--color-gray-300);
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list.active {
+main .section .template-list-search-bar-wrapper .task-dropdown-list.active {
max-height: 400px;
padding: 12px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option {
+main .section .template-list-search-bar-wrapper .task-dropdown-list li.option {
font-size: 14px;
font-weight: 400;
font-family: var(--body-font-family);
@@ -1076,49 +868,12 @@ main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dro
align-items: center;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option:hover,
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option.active
-{
+main .section .template-list-search-bar-wrapper .task-dropdown-list li.option:hover,
+main .section .template-list-search-bar-wrapper .task-dropdown-list li.option.active {
background-color: var(--color-gray-200);
color: var(--color-info-accent);
}
-/*main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option .option-radio {*/
-/* display: block;*/
-/* height: 12px;*/
-/* width: 12px;*/
-/* color: var(--color-white);*/
-/* font-size: 4px;*/
-/* border: var(--color-black) 2px solid;*/
-/* border-radius: 50%;*/
-/* margin-right: 10px;*/
-/*}*/
-
-/*main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option:hover .option-radio {*/
-/* border: #4646C6 6px solid;*/
-/* max-height: 4px;*/
-/* max-width: 4px;*/
-/* border-radius: 50%;*/
-/*}*/
-
-/*main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option:active .option-radio {*/
-/* border: #3D3DB4 6px solid;*/
-/* max-height: 4px;*/
-/* max-width: 4px;*/
-/* border-radius: 50%;*/
-/*}*/
-
-/*main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option .option-radio:focus-visible {*/
-/* outline-offset: 4px;*/
-/*}*/
-
-/*main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list li.option.active .option-radio {*/
-/* border: var(--color-info-accent) 6px solid;*/
-/* max-height: 4px;*/
-/* max-width: 4px;*/
-/* border-radius: 50%;*/
-/*}*/
-
main .template-list-fullwidth-apipowered-container .animated-template-text {
margin-top: -28px;
background: linear-gradient(320deg, #7C84F3, #FF4DD2, #FF993B, #FF4DD2, #7C84F3, #FF4DD2, #FF993B);
@@ -1131,23 +886,6 @@ main .template-list-fullwidth-apipowered-container .animated-template-text {
transition: width .3s, margin .3s, min-width .3s, background-color .3s, color .3s, border .3s, background-position 2s ease-out, padding-left .3s;
}
-@media (min-width: 600px) {
- main .template-list-fullwidth-apipowered-container .search-bar-wrapper .task-dropdown-list {
- right: 0;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper .task-dropdown-list {
- left: 0;
- }
-
- main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking {
- padding-left: 40px;
- padding-right: 40px;
- margin: 0;
- border-radius: 0;
- }
-}
-
main .template-list-fullwidth-apipowered-container .toolbar-wrapper {
padding: 0;
background: var(--color-gray-100);
@@ -1159,7 +897,7 @@ main .template-list-fullwidth-apipowered-container .toolbar-wrapper {
main .template-list-fullwidth-apipowered-container .api-templates-toolbar {
position: relative;
z-index: 1;
- margin: 28px 0px;
+ margin: 16px 0 28px 0;
padding: 16px;
display: flex;
flex-wrap: wrap;
@@ -1200,7 +938,7 @@ main .template-list-fullwidth-apipowered-container .api-templates-toolbar h2 {
line-height: 24px;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper {
margin: 0;
width: 100%;
position: absolute;
@@ -1210,39 +948,39 @@ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .searc
padding: 0;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper.collapsed .icon-search {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper.collapsed .icon-search {
left: 12px;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper .icon-search {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper .icon-search {
left: 132px;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper .icon-search-clear {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper .icon-search-clear {
right: 12px;
}
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper .icon-search-clear,
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.sticky-search-bar.collapsed .icon-search-clear,
-main .template-list-fullwidth-apipowered-container .search-bar-wrapper.sticky-search-bar.collapsed .search-dropdown {
+main .section .template-list-search-bar-wrapper .icon-search-clear,
+main .section .template-list-search-bar-wrapper.sticky-search-bar.collapsed .icon-search-clear,
+main .section .template-list-search-bar-wrapper.sticky-search-bar.collapsed .search-dropdown {
display: none;
}
-main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking .search-bar-wrapper {
+main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking .template-list-search-bar-wrapper {
display: unset;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper.collapsed {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper.collapsed {
max-width: 42px;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper.collapsed .search-bar {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper.collapsed .search-bar {
padding: 0;
max-width: 42px;
}
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper.collapsed .search-bar::placeholder,
-main .template-list-fullwidth-apipowered-container .api-templates-toolbar .search-bar-wrapper.collapsed .search-bar {
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper.collapsed .search-bar::placeholder,
+main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper.collapsed .search-bar {
color: transparent;
}
@@ -1783,17 +1521,39 @@ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-c
white-space: nowrap;
}
+main .template-list-wrapper .category-list-wrapper .category-list-heading {
+ margin-bottom: 4px;
+ height: 24px;
+ padding: 4px 7px;
+ border-radius: 8px;
+ transition: background-color .2s, color .2s;
+ overflow: hidden;
+ display: flex;
+ font-weight: 700;
+ font-size: 14px;
+ align-items: center;
+ width: max-content;
+}
+
+main .template-list-wrapper .category-list-wrapper .category-list-heading .icon {
+ height: 18px;
+ width: 18px;
+ padding-right: 8px;
+}
+
main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list ul {
+ position: relative;
list-style: none;
max-height: 800px;
overflow: hidden;
transition: max-height 0.2s;
+ margin: 0;
padding-left: 0;
}
main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list ul > li {
margin-bottom: 4px;
- padding: 4px 8px;
+ padding: 4px 7px;
border-radius: 8px;
transition: background-color .2s, color .2s;
}
@@ -1812,7 +1572,8 @@ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-c
height: 18px;
width: 18px;
display: block;
- padding-right: 6px;
+ padding-right: 8px;
+ /* padding-right: 6px; */
}
main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list ul > li > a {
@@ -1825,6 +1586,7 @@ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-c
user-select: none;
display: flex;
align-items: center;
+ white-space: nowrap;
}
main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list ul > li > a > .category-list-template-count {
@@ -1873,25 +1635,6 @@ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-c
padding-bottom: 4px;
}
-@media (min-width: 600px) {
- main .template-list-fullwidth-apipowered-container .toolbar-wrapper {
- margin: 40px;
- border-radius: 12px;
- }
-
- main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only {
- display: unset;
- }
-}
-
-@media (min-width: 900px) {
- main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list-wrapper {
- text-align: left;
- padding-top: 32px;
- padding-left: 12px;
- }
-}
-
/* Carousel styles (Template-list specific) */
/* hide controls in mobile-breakpoint if they scrolled using drag */
@@ -1936,165 +1679,13 @@ main .template-list.horizontal .carousel-platform {
scroll-padding: 12px;
}
-@media (min-width: 1200px) {
- main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-left,
- main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-right {
- opacity: unset;
+@media (max-width: 600px) {
+ main .section.template-list-horizontal-container > div.template-list-wrapper {
+ margin-left: 0;
+ margin-right: 0;
+ max-width: unset;
}
-
- main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-left a.button.carousel-arrow,
- main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-right a.button.carousel-arrow {
- pointer-events: auto;
- }
-
- main .template-list.horizontal .carousel-container .carousel-fader-left.arrow-hidden,
- main .template-list.horizontal .carousel-container .carousel-fader-right.arrow-hidden {
- opacity: 0;
- pointer-events: none;
- }
-
- main .template-list.horizontal .carousel-container .carousel-fader-left.arrow-hidden a.button.carousel-arrow,
- main .template-list.horizontal .carousel-container .carousel-fader-right.arrow-hidden a.button.carousel-arrow {
- pointer-events: none;
- }
-
- main .template-list.horizontal .carousel-container.controls-hidden .carousel-platform {
- scroll-snap-type: x mandatory;
- }
-
- main .template-list.horizontal .carousel-container {
- margin-left: auto;
- margin-right: auto;
- max-width: 470px;
- display: block;
- }
-
- main .template-list.horizontal.fullwidth .carousel-container {
- margin-bottom: 20px;
- max-width: none;
- display: block;
- }
-
- main .template-list.horizontal .carousel-container .carousel-fader-left {
- background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
- }
-
- main .template-list.horizontal.fullwidth .carousel-container .carousel-fader-left {
- width: 150px;
- background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
- }
-
- main .template-list.horizontal .carousel-container .carousel-fader-right {
- background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
- }
-
- main .template-list.horizontal.fullwidth .carousel-container .carousel-fader-left {
- background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
- width: 150px;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar {
- border-radius: 12px;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-functions .functions-container {
- display: flex;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer {
- display: none;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-content-search {
- flex-basis: unset;
- padding-right: 16px;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer .function-sort:hover .options-wrapper {
- display: flex;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer .function-sort:hover .button-wrapper .icon.icon-drop-down-arrow {
- transform: rotate(180deg);
- }
-}
-
-@media (min-width: 900px) {
- main .template-list.horizontal .carousel-container {
- margin-left: 12px;
- margin-right: 12px;
- max-width: none;
- display: inline-block;
- }
-
- main .section.template-list-horizontal-container {
- padding-left: 32px;
- padding-right: 32px;
- }
-
- main .section.template-list-horizontal-container > div {
- max-width: 836px;
- }
-
- main .section.template-list-horizontal-container > div > *:not(.template-list.horizontal),
- main .section.template-list-horizontal-container > div > .template-list.horizontal > *:not(.carousel-container) {
- max-width: unset;
- }
-
- main .template-list.horizontal {
- max-width: 800px;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar h2 {
- font-size: 22px;
- line-height: 24px;
- width: max-content;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-functions {
- flex-grow: 0;
- }
-
- main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking .wrapper-content-search {
- width: auto;
- }
-
- main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-content-search .search-bar-wrapper {
- width: 480px;
- }
-
- main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .search-dropdown-heading {
- display: unset;
- }
-
- main .template-list-fullwidth-apipowered-container .search-bar-wrapper .search-dropdown .free-plan-widget {
- gap: 32px;
- }
-}
-
-@media (min-width: 1200px) {
- main .section.template-list-horizontal-container > div {
- max-width: 1024px;
- }
-
- main .template-list.horizontal.fullwidth > div,
- main .section.template-list-horizontal-fullwidth-container > div,
- main .section.template-list-horizontal-fullwidth-collaboration-container > div {
- max-width: none;
- }
-
- main .template-list.horizontal {
- max-width: 1090px;
- }
-}
-
-@media (max-width: 600px) {
- main .section.template-list-horizontal-container > div.template-list-wrapper {
- margin-left: 0;
- margin-right: 0;
- max-width: unset;
- }
-}
+}
main .template-list.mini {
min-height: unset;
@@ -2228,34 +1819,6 @@ main .template-list.collaboration .carousel-container {
margin-left: 6px;
}
-@media (min-width: 900px) {
- main .template-list.collaboration .carousel-container {
- margin-left: 0;
- }
-}
-
-@media (min-width: 1200px) {
- main .template-list-horizontal-fullwidth-mini-container .template-list.mini {
- display: none;
- }
-
- main .template-list.collaboration .template-title > div > h3 {
- gap: 12px;
- }
-
- main .template-list.collaboration .template-title > div > h3 .collaboration-anchor .clipboard-tag {
- right: unset;
- bottom: unset;
- top: 50%;
- left: 48px;
- transform: translateY(-50%);
- }
-
- main .template-list.collaboration .template-title > div > h3 .collaboration-anchor.copied .clipboard-tag {
- left: 28px;
- }
-}
-
main .template-list-horizontal-apipowered-holiday-container {
padding-bottom: 10px;
position: relative;
@@ -2421,31 +1984,479 @@ main .template-list-horizontal-apipowered-holiday-container.expanded .animation-
transition: opacity 1s;
}
+main .template-list-fullwidth-apipowered-container .template-list-fallback-msg-wrapper {
+ font-size: 22px;
+ text-align: center;
+ padding: 0 28px;
+}
+
+main .template-list-fullwidth-apipowered-container nav ol.templates-breadcrumbs {
+ list-style: none;
+ margin: 0;
+ padding: 0 0 0 40px;
+ display: flex;
+}
+
+main .template-list-fullwidth-apipowered-container nav ol.templates-breadcrumbs li {
+ display: flex;
+ padding: 5px 10px 5px 0;
+ color: var(--color-gray-500);
+}
+
+main .template-list-fullwidth-apipowered-container nav ol.templates-breadcrumbs li a {
+ color: var(--color-black);
+ font-weight: 400;
+}
+
+main .template-list-fullwidth-apipowered-container nav ol.templates-breadcrumbs li:not(:first-child):before {
+ content: '/';
+ color: var(--color-black);
+ padding: 0 10px 0 0;
+}
+
@media (min-width: 600px) {
- main .template-list.horizontal.holiday .carousel-container .carousel-fader-left,
- main .template-list.horizontal.holiday .carousel-container .carousel-fader-right {
- background: initial;
+ main .template-list.fullwidth.lg-view .template.placeholder {
+ width: 352px;
}
-}
-@media (min-width: 1200px) {
- main .template-list-horizontal-apipowered-holiday-container {
- padding-bottom: 0;
+ main .template-list.fullwidth.md-view .template.placeholder {
+ width: 227.33px;
}
- main .template-list-horizontal-apipowered-holiday-container .toggle-bar {
- max-width: 1024px;
+ main .template-list.fullwidth.sm-view .template.placeholder {
+ width: 165px;
}
- main .template-list-horizontal-apipowered-holiday-container .mobile-only {
- display: none;
+ main .template-list.fullwidth.lg-view > .masonry-col {
+ max-width: 328px;
+ text-align: center;
}
- main .template-list-horizontal-apipowered-holiday-container .toggle-bar-bottom {
- display: flex;
+ main .template-list.fullwidth.md-view > .masonry-col {
+ max-width: 200px;
+ text-align: center;
}
- main .template-list-horizontal-apipowered-holiday-container .template-list-wrapper.expanded {
- padding-top: 0;
+ main .template-list.fullwidth.sm-view > .masonry-col {
+ max-width: 156px;
+ text-align: center;
+ }
+
+ main .template-list.fullwidth.lg-view .template.placeholder,
+ main .template-list.fullwidth.md-view .template.placeholder,
+ main .template-list.fullwidth.sm-view .template.placeholder {
+ max-height: unset;
+ }
+
+ main .template-list.sm-view .template.placeholder > div:first-of-type {
+ height: 95%;
+ }
+
+ main .section .template-list-search-bar-wrapper .task-dropdown-list {
+ right: 0;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .template-list-search-bar-wrapper .task-dropdown-list {
+ left: 0;
+ }
+
+ main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking {
+ padding-left: 40px;
+ padding-right: 40px;
+ margin: 0;
+ border-radius: 0;
+ z-index: 2;
+ }
+
+ main .template-list-fullwidth-apipowered-container .toolbar-wrapper {
+ margin: 0 40px;
+ border-radius: 12px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list::before {
+ content: '';
+ z-index: 0;
+ position: absolute;
+ height: 100%;
+ transition: opacity 0.2s;
+ background: linear-gradient(180deg, #FFFFFF00 0%, #FFFFFF 59%);
+ }
+
+ main .template-list-wrapper.with-categories-list.with-fallback-msg .category-list-wrapper.desktop-only {
+ top: -56px;
+ }
+
+ main .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only:not(:hover) .category-list::before {
+ width: 100%;
+ }
+
+ main .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only:hover .category-list::before {
+ width: 0;
+ }
+
+ main .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only:hover::before {
+ top: 0;
+ content: '';
+ opacity: 1;
+ z-index: -1;
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ transition: opacity 0.2s;
+ background: linear-gradient(90deg, #ffffff 50%, #ffffff9B 88%, #ffffff00 100%);
+ }
+
+ main .template-list-fullwidth-apipowered-container .category-list-wrapper.desktop-only .category-list-toggle-wrapper {
+ padding: 4px 8px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .category-list-wrapper.desktop-only .category-list-toggle-wrapper .icon {
+ transition: transform 0.2s;
+ }
+
+ main .template-list-fullwidth-apipowered-container .category-list-wrapper.desktop-only:hover .category-list-toggle-wrapper .icon {
+ transform: rotate(180deg);
+ }
+
+ main .template-list.horizontal.holiday .carousel-container .carousel-fader-left,
+ main .template-list.horizontal.holiday .carousel-container .carousel-fader-right {
+ background: initial;
+ }
+}
+
+@media (min-width: 900px) {
+ main .template-list.fullwidth.md-view .template.placeholder {
+ width: 258.5px;
+ }
+
+ main .template-list-container > div,
+ main .template-list-fourcols-container > div,
+ main .template-list-horizontal-container > div {
+ max-width: 900px;
+ }
+
+ main .template-list-sixcols-container > div {
+ max-width: 748px;
+ }
+
+ main .template-list-fullwidth-container > div,
+ main .template-list-fullwidth-container > .default-content-wrapper,
+ main .template-list-fullwidth-apipowered-container > div,
+ main .template-list-fullwidth-apipowered-container > .default-content-wrapper {
+ max-width: none;
+ }
+
+ main .template-list.sixcols > .masonry-col,
+ main .template-list.fullwidth > .masonry-col {
+ max-width: 187px;
+ }
+
+ main .template-list .template-title > div {
+ flex-direction: row;
+ justify-content: space-between;
+ }
+
+ main .template-list .template-title > div > * {
+ text-align: left;
+ }
+
+ main .template-list.sixcols > .masonry-col,
+ main .template-list.fullwidth > .masonry-col {
+ max-width: 187px;
+ }
+
+ main .template-list.fullwidth.md-view > .masonry-col {
+ max-width: 258.5px;
+ text-align: center;
+ }
+
+ main .template-list .template {
+ width: 240px;
+ margin-left: 20px;
+ margin-right: 20px;
+ }
+
+ main .template-list.sixcols .template:not(.placeholder),
+ main .template-list.fullwidth .template:not(.placeholder) {
+ margin: 11px 11px 9px 11px; /* make up for 4px height overlap in first child div */
+ }
+
+ main .template-list.sixcols .template.placeholder,
+ main .template-list.fullwidth .template.placeholder {
+ width: 145px;
+ margin: 21px;
+ }
+
+ /* template-list inside columns */
+
+ main .columns .template-list {
+ margin-top: 0;
+ margin-left: 40px;
+ }
+
+ main .template-list .template:not(.placeholder) .icon-free-badge {
+ display: none;
+ }
+
+ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list-wrapper {
+ text-align: left;
+ padding-top: 32px;
+ padding-left: 40px;
+ }
+
+ main .template-list.horizontal .carousel-container {
+ margin-left: 12px;
+ margin-right: 12px;
+ max-width: none;
+ display: inline-block;
+ }
+
+ main .section.template-list-horizontal-container {
+ padding-left: 32px;
+ padding-right: 32px;
+ }
+
+ main .section.template-list-horizontal-container > div {
+ max-width: 836px;
+ }
+
+ main .section.template-list-horizontal-container > div > *:not(.template-list.horizontal),
+ main .section.template-list-horizontal-container > div > .template-list.horizontal > *:not(.carousel-container) {
+ max-width: unset;
+ }
+
+ main .template-list.horizontal {
+ max-width: 800px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar h2 {
+ font-size: 22px;
+ line-height: 24px;
+ width: max-content;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-functions {
+ flex-grow: 0;
+ }
+
+ main .template-list-fullwidth-apipowered-container .toolbar-wrapper.sticking .wrapper-content-search {
+ width: auto;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-content-search .template-list-search-bar-wrapper {
+ width: 480px;
+ }
+
+ main .section .template-list-search-bar-wrapper .search-dropdown .search-dropdown-heading {
+ display: unset;
+ }
+
+ main .section .template-list-search-bar-wrapper .search-dropdown .free-plan-widget {
+ gap: 32px;
+ }
+
+ main .template-list.collaboration .carousel-container {
+ margin-left: 0;
+ }
+}
+
+@media (min-width: 1200px) {
+ main .template-list-fourcols-container > div {
+ max-width: 1200px;
+ }
+
+ main .template-list-sixcols-container > div {
+ max-width: 1122px;
+ }
+
+ main .template-list-fullwidth-container > div,
+ main .template-list-fullwidth-apipowered-container > div {
+ max-width: none;
+ }
+
+ /* template-list inside columns */
+
+ main .columns .template-list {
+ flex-direction: row;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ }
+
+ main .columns .template-list .template {
+ justify-content: flex-start;
+ min-width: 132px;
+ }
+
+ main .columns .template-list .template-link {
+ font-size: 0.875rem;
+ font-weight: 400;
+ }
+
+ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only {
+ display: unset;
+ background: transparent;
+ position: absolute;
+ left: 0;
+ /* top: 64px; */
+ height: calc(100% - 64px);
+ max-height: unset;
+ overflow: hidden;
+ min-width: unset;
+ max-width: 32px;
+ z-index: 1;
+ }
+
+ main .template-list-fullwidth-apipowered-container .template-list-wrapper.with-categories-list .category-list-wrapper.desktop-only:hover {
+ min-width: max-content;
+ padding-right: 160px;
+ overflow: visible;
+ }
+
+ main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-left,
+ main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-right {
+ opacity: unset;
+ }
+
+ main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-left a.button.carousel-arrow,
+ main .template-list.horizontal .carousel-container.controls-hidden .carousel-fader-right a.button.carousel-arrow {
+ pointer-events: auto;
+ }
+
+ main .template-list.horizontal .carousel-container .carousel-fader-left.arrow-hidden,
+ main .template-list.horizontal .carousel-container .carousel-fader-right.arrow-hidden {
+ opacity: 0;
+ pointer-events: none;
+ }
+
+ main .template-list.horizontal .carousel-container .carousel-fader-left.arrow-hidden a.button.carousel-arrow,
+ main .template-list.horizontal .carousel-container .carousel-fader-right.arrow-hidden a.button.carousel-arrow {
+ pointer-events: none;
+ }
+
+ main .template-list.horizontal .carousel-container.controls-hidden .carousel-platform {
+ scroll-snap-type: x mandatory;
+ }
+
+ main .template-list.horizontal .carousel-container {
+ margin-left: auto;
+ margin-right: auto;
+ max-width: none;
+ display: block;
+ }
+
+ main .template-list.horizontal.fullwidth .carousel-container {
+ margin-bottom: 20px;
+ max-width: none;
+ display: block;
+ }
+
+ main .template-list.fullwidth.apipowered {
+ padding-left: 56px;
+ }
+
+ main .template-list.horizontal .carousel-container .carousel-fader-left {
+ background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
+ }
+
+ main .template-list.horizontal.fullwidth .carousel-container .carousel-fader-left {
+ width: 150px;
+ background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
+ }
+
+ main .template-list.horizontal .carousel-container .carousel-fader-right {
+ background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
+ }
+
+ main .template-list.horizontal.fullwidth .carousel-container .carousel-fader-left {
+ background: linear-gradient(to left, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
+ width: 150px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar {
+ border-radius: 12px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-functions .functions-container {
+ display: flex;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer {
+ display: none;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .wrapper-content-search {
+ flex-basis: unset;
+ padding-right: 16px;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer .function-sort:hover .options-wrapper {
+ display: flex;
+ }
+
+ main .template-list-fullwidth-apipowered-container .api-templates-toolbar .functions-drawer .function-sort:hover .button-wrapper .icon.icon-drop-down-arrow {
+ transform: rotate(180deg);
+ }
+
+ main .template-list.fullwidth.md-view > .masonry-col {
+ max-width: 250px;
+ }
+
+ main .section.template-list-horizontal-container > div {
+ max-width: 1024px;
+ }
+
+ main .template-list.horizontal.fullwidth > div,
+ main .section.template-list-horizontal-fullwidth-container > div,
+ main .section.template-list-horizontal-fullwidth-collaboration-container > div {
+ max-width: none;
+ }
+
+ main .template-list.horizontal {
+ max-width: 1090px;
+ }
+
+ main .template-list-horizontal-fullwidth-mini-container .template-list.mini {
+ display: none;
+ }
+
+ main .template-list.collaboration .template-title > div > h3 {
+ gap: 12px;
+ }
+
+ main .template-list.collaboration .template-title > div > h3 .collaboration-anchor .clipboard-tag {
+ right: unset;
+ bottom: unset;
+ top: 50%;
+ left: 48px;
+ transform: translateY(-50%);
+ }
+
+ main .template-list.collaboration .template-title > div > h3 .collaboration-anchor.copied .clipboard-tag {
+ left: 28px;
+ }
+
+ main .template-list-horizontal-apipowered-holiday-container {
+ padding-bottom: 0;
+ }
+
+ main .template-list-horizontal-apipowered-holiday-container .toggle-bar {
+ max-width: 1024px;
+ }
+
+ main .template-list-horizontal-apipowered-holiday-container .mobile-only {
+ display: none;
+ }
+
+ main .template-list-horizontal-apipowered-holiday-container .toggle-bar-bottom {
+ display: flex;
+ }
+
+ main .template-list-horizontal-apipowered-holiday-container .template-list-wrapper.expanded {
+ padding-top: 0;
+ }
+
+ main .section.template-list-fullwidth-apipowered-container .template-list-fallback-msg-wrapper {
+ padding-left: 120px;
+ text-align: left;
}
}
diff --git a/express/blocks/template-list/template-list.js b/express/blocks/template-list/template-list.js
index 4b23ab18e..b7076137a 100644
--- a/express/blocks/template-list/template-list.js
+++ b/express/blocks/template-list/template-list.js
@@ -9,7 +9,7 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
-/* eslint-disable import/named, import/extensions */
+/* eslint-disable import/named, import/extensions, no-underscore-dangle */
import {
addAnimationToggle,
@@ -33,21 +33,9 @@ import {
import { Masonry } from '../shared/masonry.js';
import { buildCarousel } from '../shared/carousel.js';
-
-const props = {
- templates: [],
- filters: { locales: '(en)' },
- tailButton: '',
- limit: 70,
- total: 0,
- start: '',
- sort: '-_score,-remixCount',
- masonry: undefined,
- authoringError: false,
- headingTitle: null,
- headingSlug: null,
- viewAllLink: null,
-};
+import fetchAllTemplatesMetadata from '../../scripts/all-templates-metadata.js';
+import { memoize } from '../../scripts/utils.js';
+import getBreadcrumbs from './breadcrumbs.js';
function wordStartsWithVowels(word) {
return word.match('^[aieouâêîôûäëïöüàéèùœAIEOUÂÊÎÔÛÄËÏÖÜÀÉÈÙŒ].*');
@@ -68,10 +56,19 @@ function trimFormattedFilterText(attr, capitalize) {
return capitalize ? resultString.charAt(0).toUpperCase() + resultString.slice(1) : resultString;
}
-async function populateHeadingPlaceholder(locale) {
+function loadBetterAssetInBackground(img) {
+ const updateImgRes = () => {
+ img.src = img.src.replace('width/size/151', 'width/size/400');
+ img.removeEventListener('load', updateImgRes);
+ };
+
+ img.addEventListener('load', updateImgRes);
+}
+
+async function populateHeadingPlaceholder(locale, props) {
const heading = props.heading.replace("''", '');
// special treatment for express/ root url
- const camelHeading = heading === 'Adobe Express' ? heading : heading.charAt(0).toLowerCase() + heading.slice(1);
+ const lowerCaseHeading = heading === 'Adobe Express' ? heading : heading.toLowerCase();
const placeholders = await fetchPlaceholders();
const lang = getLanguage(getLocale(window.location));
const templateCount = lang === 'es-ES' ? props.total.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') : props.total.toLocaleString(lang);
@@ -85,9 +82,9 @@ async function populateHeadingPlaceholder(locale) {
if (grammarTemplate) {
grammarTemplate = grammarTemplate
- .replace('{{quantity}}', templateCount)
+ .replace('{{quantity}}', props.fallbackMsg ? '0' : templateCount)
.replace('{{Type}}', heading)
- .replace('{{type}}', camelHeading);
+ .replace('{{type}}', lowerCaseHeading);
if (locale === 'fr') {
grammarTemplate.split(' ').forEach((word, index, words) => {
@@ -117,28 +114,54 @@ function formatSearchQuery(limit, start, sort, filters) {
return `https://www.adobe.com/cc-express-search-api?limit=${limit}&start=${start}&orderBy=${sort}&filters=${filterString}`;
}
-async function fetchTemplates() {
- if (!props.authoringError && Object.keys(props.filters).length !== 0) {
- props.queryString = formatSearchQuery(props.limit, props.start, props.sort, props.filters);
+const memoizedFetchUrl = memoize((url) => fetch(url).then((r) => (r.ok ? r.json() : null)), {
+ key: (q) => q,
+ ttl: 1000 * 60 * 60 * 24,
+});
- const result = await fetch(props.queryString)
- .then((response) => response.json())
- .then((response) => response);
+async function getFallbackMsg(tasks = '') {
+ const placeholders = await fetchPlaceholders();
+ const fallBacktextTemplate = tasks && tasks !== "''" ? placeholders['templates-fallback-with-tasks'] : placeholders['templates-fallback-without-tasks'];
- // eslint-disable-next-line no-underscore-dangle
- if (result._embedded.total > 0) {
+ if (fallBacktextTemplate) {
+ return tasks ? fallBacktextTemplate.replaceAll('{{tasks}}', tasks.toString()) : fallBacktextTemplate;
+ }
+
+ return `Sorry we couldn't find any results for what you searched for, try some of these popular ${
+ tasks ? ` ${tasks.toString()} ` : ''}templates instead.`;
+}
+
+async function fetchTemplates(props) {
+ props.fallbackMsg = null;
+ if (props.authoringError || Object.keys(props.filters).length === 0) {
+ props.authoringError = true;
+ props.heading = 'Authoring error: first row must specify the template “type”';
+ return null;
+ }
+ const { limit, start, sort } = props;
+ props.queryString = formatSearchQuery(limit, start, sort, props.filters);
+
+ let result = await memoizedFetchUrl(props.queryString);
+
+ if (result?._embedded?.total) {
+ return result;
+ }
+ const { filters: { tasks, locales } } = props;
+ const tasksMatch = /\((.+)\)/.exec(tasks);
+ if (tasksMatch) {
+ props.queryString = formatSearchQuery(limit, start, sort, { locales, tasks });
+ result = await memoizedFetchUrl(props.queryString);
+ if (result?._embedded?.total) {
+ props.fallbackMsg = await getFallbackMsg(tasksMatch[1]);
return result;
- } else {
- // save fetch if search query returned 0 templates. "Bad result is better than no result"
- return fetch(`https://www.adobe.com/cc-express-search-api?limit=${props.limit}&start=${props.start}&orderBy=${props.sort}&filters=locales:(${props.filters.locales})`)
- .then((response) => response.json())
- .then((response) => response);
}
}
- return null;
+ props.queryString = formatSearchQuery(limit, start, sort, { locales });
+ props.fallbackMsg = await getFallbackMsg();
+ return memoizedFetchUrl(props.queryString);
}
-function fetchTemplatesByTasks(tasks) {
+function fetchTemplatesByTasks(tasks, props) {
const tempFilters = { ...props.filters };
if (tasks) {
@@ -146,46 +169,39 @@ function fetchTemplatesByTasks(tasks) {
}
if (!props.authoringError && Object.keys(tempFilters).length !== 0) {
- const tempQ = formatSearchQuery(props.limit, '', props.sort, tempFilters);
+ const tempQ = formatSearchQuery(0, '', props.sort, tempFilters);
- return fetch(tempQ)
- .then((response) => response.json())
- .then((response) => response);
+ return memoizedFetchUrl(tempQ);
}
return null;
}
-async function appendCategoryTemplatesCount($section) {
+async function appendCategoryTemplatesCount($section, props) {
const categories = $section.querySelectorAll('ul.category-list > li');
- const currentTask = props.filters.tasks;
const lang = getLanguage(getLocale(window.location));
for (const li of categories) {
const anchor = li.querySelector('a');
if (anchor) {
// eslint-disable-next-line no-await-in-loop
- const json = await fetchTemplatesByTasks(anchor.dataset.tasks);
+ const json = await fetchTemplatesByTasks(anchor.dataset.tasks, props);
const countSpan = createTag('span', { class: 'category-list-template-count' });
- // eslint-disable-next-line no-underscore-dangle
- countSpan.textContent = `(${json._embedded.total.toLocaleString(lang)})`;
+ countSpan.textContent = `(${json?._embedded?.total?.toLocaleString(lang) ?? 0})`;
anchor.append(countSpan);
}
}
-
- props.filters.tasks = currentTask;
}
-async function processResponse() {
- const placeholders = await fetchPlaceholders();
- const response = await fetchTemplates();
- let templateFetched;
- if (response) {
- // eslint-disable-next-line no-underscore-dangle
- templateFetched = response._embedded.results;
+async function processResponse(props) {
+ const [placeholders, response] = await Promise.all([fetchPlaceholders(), fetchTemplates(props)]);
+ const { _embedded } = response || {};
+ let templateFetched = [];
+ if (_embedded) {
+ const { results, total } = _embedded;
+ templateFetched = results;
if ('_links' in response) {
- // eslint-disable-next-line no-underscore-dangle
const nextQuery = response._links.next.href;
const starts = new URLSearchParams(nextQuery).get('start').split(',');
starts.pop();
@@ -194,25 +210,18 @@ async function processResponse() {
props.start = '';
}
- // eslint-disable-next-line no-underscore-dangle
- props.total = response._embedded.total;
+ props.total = total;
}
- const renditionParams = {
- format: 'jpg',
- dimension: 'width',
- size: 400,
- };
-
if (templateFetched) {
return templateFetched.map((template) => {
const $template = createTag('div');
- const $pictureWrapper = createTag('div');
+ const imgWrapper = createTag('div');
['format', 'dimension', 'size'].forEach((param) => {
- template.rendition.href = template.rendition.href.replace(`{${param}}`, renditionParams[param]);
+ template.rendition.href = template.rendition.href.replace(`{${param}}`, props.renditionParams[param]);
});
- const $picture = createTag('img', {
+ const img = createTag('img', {
src: template.rendition.href,
alt: template.title,
});
@@ -224,10 +233,10 @@ async function processResponse() {
});
$button.textContent = placeholders['edit-this-template'] ?? 'Edit this template';
- $pictureWrapper.insertAdjacentElement('beforeend', $picture);
- $buttonWrapper.insertAdjacentElement('beforeend', $button);
- $template.insertAdjacentElement('beforeend', $pictureWrapper);
- $template.insertAdjacentElement('beforeend', $buttonWrapper);
+ imgWrapper.append(img);
+ $buttonWrapper.append($button);
+ $template.append(imgWrapper, $buttonWrapper);
+ loadBetterAssetInBackground(img);
return $template;
});
} else {
@@ -252,7 +261,7 @@ async function fetchBlueprint(pathname) {
return ($main);
}
-function populateTemplates($block, templates) {
+function populateTemplates($block, templates, props) {
for (let $tmplt of templates) {
const isPlaceholder = $tmplt.querySelector(':scope > div:first-of-type > img[src*=".svg"], :scope > div:first-of-type > svg');
const $linkContainer = $tmplt.querySelector(':scope > div:nth-of-type(2)');
@@ -292,11 +301,11 @@ function populateTemplates($block, templates) {
if (isPlaceholder) {
// add aspect ratio to template
const sep = option.includes(':') ? ':' : 'x';
- const ratios = option.split(sep).map((e) => +e);
+ const ratios = option.split(sep).map((str) => parseInt(str, 10));
props.placeholderFormat = ratios;
if ($block.classList.contains('horizontal')) {
const height = $block.classList.contains('mini') ? 100 : 200;
- if (ratios[1]) {
+ if (ratios?.length === 2) {
const width = (ratios[0] / ratios[1]) * height;
$tmplt.style = `width: ${width}px`;
if (width / height > 1.3) {
@@ -423,7 +432,7 @@ async function attachFreeInAppPills($block) {
}
}
-async function readRowsFromBlock($block) {
+async function readRowsFromBlock($block, props) {
if ($block.children.length > 0) {
Array.from($block.children).forEach((row, index, array) => {
const cells = row.querySelectorAll('div');
@@ -456,13 +465,12 @@ async function readRowsFromBlock($block) {
}
});
- const fetchedTemplates = await processResponse();
+ const fetchedTemplates = await processResponse(props);
if (fetchedTemplates) {
props.templates = props.templates.concat(fetchedTemplates);
props.templates.forEach((template) => {
- const clone = template.cloneNode(true);
- $block.append(clone);
+ $block.append(template);
});
}
} else {
@@ -471,11 +479,45 @@ async function readRowsFromBlock($block) {
}
}
-async function redirectSearch($searchBar) {
- const placeholders = await fetchPlaceholders().then((result) => result);
+function getRedirectUrl(tasks, topics, format, allTemplatesMetadata) {
+ const locale = getLocale(window.location);
+ const urlPrefix = locale === 'us' ? '' : `/${locale}`;
+ const topicUrl = topics ? `/${topics}` : '';
+ const taskUrl = `/${handlelize(tasks.toLowerCase())}`;
+ const targetPath = `${urlPrefix}/express/templates${taskUrl}${topicUrl}`;
+ const pathMatch = (e) => e.path === targetPath;
+ if (allTemplatesMetadata.some(pathMatch)) {
+ return `${window.location.origin}${targetPath}`;
+ } else {
+ const searchUrlTemplate = `/express/templates/search?tasks=${tasks}&phformat=${format}&topics=${topics || "''"}`;
+ const searchUrl = `${window.location.origin}${urlPrefix}${searchUrlTemplate}`;
+ return searchUrl;
+ }
+}
+
+function logSearch(form, url = 'https://main--express-website--adobe.hlx.page/express/search-terms-log') {
+ if (form) {
+ const input = form.querySelector('input');
+ fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ data: {
+ keyword: input.value,
+ locale: getLocale(window.location),
+ timestamp: Date.now(),
+ audience: document.body.dataset.device,
+ },
+ }),
+ });
+ }
+}
+
+async function redirectSearch($searchBar, props) {
+ const placeholders = await fetchPlaceholders();
const taskMap = JSON.parse(placeholders['task-name-mapping']);
if ($searchBar) {
- const wrapper = $searchBar.closest('.search-bar-wrapper');
+ const wrapper = $searchBar.closest('.template-list-search-bar-wrapper');
const $selectorTask = wrapper.querySelector('.task-dropdown-list > .option.active');
props.filters.tasks = `(${$selectorTask.dataset.tasks})`;
}
@@ -498,20 +540,9 @@ async function redirectSearch($searchBar) {
searchInput = searchInput.trim();
[[currentTasks]] = tasksFoundInInput;
}
-
- const locale = getLocale(window.location);
- const urlPrefix = locale === 'us' ? '' : `/${locale}`;
- const topicUrl = searchInput ? `/${searchInput}` : '';
- const taskUrl = `/${handlelize(currentTasks.toLowerCase())}`;
- const searchUrlTemplate = `/express/templates/search?tasks=${currentTasks}&phformat=${format}&topics=${searchInput || "''"}`;
- const targetPath = `${urlPrefix}/express/templates${taskUrl}${topicUrl}`;
- const searchUrl = `${window.location.origin}${urlPrefix}${searchUrlTemplate}`;
- const pathMatch = (e) => e.path === targetPath;
- if (window.templates && window.templates.data.some(pathMatch)) {
- window.location = `${window.location.origin}${targetPath}`;
- } else {
- window.location = searchUrl;
- }
+ const allTemplatesMetadata = await fetchAllTemplatesMetadata();
+ const redirectUrl = getRedirectUrl(currentTasks, searchInput, format, allTemplatesMetadata);
+ window.location = redirectUrl;
}
function makeTemplateFunctions(placeholders) {
@@ -535,25 +566,25 @@ function makeTemplateFunctions(placeholders) {
Object.entries(functions).forEach((entry) => {
entry[1].elements.wrapper = createTag('div', {
- class: `function-wrapper function-${Object.values(entry)[0]}`,
- 'data-param': Object.values(entry)[0],
+ class: `function-wrapper function-${entry[0]}`,
+ 'data-param': entry[0],
});
entry[1].elements.wrapper.subElements = {
button: {
- wrapper: createTag('div', { class: `button-wrapper button-wrapper-${Object.values(entry)[0]}` }),
+ wrapper: createTag('div', { class: `button-wrapper button-wrapper-${entry[0]}` }),
subElements: {
iconHolder: createTag('span', { class: 'icon-holder' }),
- textSpan: createTag('span', { class: `current-option current-option-${Object.values(entry)[0]}` }),
+ textSpan: createTag('span', { class: `current-option current-option-${entry[0]}` }),
chevIcon: getIconElement('drop-down-arrow'),
},
},
options: {
- wrapper: createTag('div', { class: `options-wrapper options-wrapper-${Object.values(entry)[0]}` }),
+ wrapper: createTag('div', { class: `options-wrapper options-wrapper-${entry[0]}` }),
subElements: Object.entries(entry[1].placeholders).map((option, subIndex) => {
const icon = getIconElement(entry[1].icons[subIndex]);
- const optionButton = createTag('div', { class: 'option-button', 'data-value': Object.values(option)[1] });
- [optionButton.textContent] = Object.values(option);
+ const optionButton = createTag('div', { class: 'option-button', 'data-value': option[1] });
+ [optionButton.textContent] = option;
optionButton.prepend(icon);
return optionButton;
}),
@@ -569,6 +600,7 @@ function makeTemplateFunctions(placeholders) {
function updateFilterIcon(block) {
const section = block.closest('.section.template-list-fullwidth-apipowered-container');
+ if (!section) return;
const functionWrapper = section.querySelectorAll('.function-wrapper');
const optionsWrapper = section.querySelectorAll('.options-wrapper');
@@ -588,22 +620,22 @@ function decorateFunctionsContainer($block, $section, functions, placeholders) {
const $functionsContainer = createTag('div', { class: 'functions-container' });
const $functionContainerMobile = createTag('div', { class: 'functions-drawer' });
- Object.entries(functions).forEach((filter) => {
- const $filterWrapper = filter[1].elements.wrapper;
+ Object.values(functions).forEach((filter) => {
+ const filterWrapper = filter.elements.wrapper;
- Object.entries($filterWrapper.subElements).forEach((part) => {
- const $innerWrapper = part[1].wrapper;
+ Object.values(filterWrapper.subElements).forEach((part) => {
+ const innerWrapper = part.wrapper;
- Object.entries(part[1].subElements).forEach((innerElement) => {
- if (Object.values(innerElement)[1]) {
- $innerWrapper.append(Object.values(innerElement)[1]);
+ Object.values(part.subElements).forEach((innerElement) => {
+ if (innerElement) {
+ innerWrapper.append(innerElement);
}
});
- $filterWrapper.append($innerWrapper);
+ filterWrapper.append(innerWrapper);
});
- $functionContainerMobile.append($filterWrapper.cloneNode({ deep: true }));
- $functionsContainer.append($filterWrapper);
+ $functionContainerMobile.append(filterWrapper.cloneNode({ deep: true }));
+ $functionsContainer.append(filterWrapper);
});
// restructure drawer for mobile design
@@ -673,6 +705,7 @@ function decorateFunctionsContainer($block, $section, functions, placeholders) {
}
function resetTaskDropdowns($section) {
+ if (!$section) return;
const $taskDropdowns = $section.querySelectorAll('.task-dropdown');
const $taskDropdownLists = $section.querySelectorAll('.task-dropdown-list');
@@ -687,7 +720,7 @@ function resetTaskDropdowns($section) {
function closeTaskDropdown($toolBar) {
const $section = $toolBar.closest('.section.template-list-fullwidth-apipowered-container');
- const $searchBarWrappers = $section.querySelectorAll('.search-bar-wrapper');
+ const $searchBarWrappers = $section.querySelectorAll('.template-list-search-bar-wrapper');
$searchBarWrappers.forEach(($wrapper) => {
const $taskDropdown = $wrapper.querySelector('.task-dropdown');
const $taskDropdownList = $taskDropdown.querySelector('.task-dropdown-list');
@@ -696,10 +729,11 @@ function closeTaskDropdown($toolBar) {
});
}
-function initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper) {
+function initSearchFunction($toolBar, $stickySearchBarWrapper, generatedSearchBar, props) {
const $section = $toolBar.closest('.section.template-list-fullwidth-apipowered-container');
const $stickySearchBar = $stickySearchBarWrapper.querySelector('input.search-bar');
- const $searchBarWrappers = $section.querySelectorAll('.search-bar-wrapper');
+ const searchMarqueeSearchBar = document.querySelector('.search-marquee .search-bar-wrapper');
+ const $searchBarWrappers = document.querySelectorAll('.template-list-search-bar-wrapper');
const $toolbarWrapper = $toolBar.parentElement;
const searchBarWatcher = new IntersectionObserver((entries) => {
@@ -711,7 +745,10 @@ function initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper
}
}, { rootMargin: '0px', threshold: 1 });
- searchBarWatcher.observe($searchBarWrapper);
+ // for backward compatibility
+ // TODO: consider removing !searchMarqueeSearchBar as it should always be there for desktop pages
+ const searchBarToWatch = (document.body.dataset.device === 'mobile' || !searchMarqueeSearchBar) ? generatedSearchBar : searchMarqueeSearchBar;
+ searchBarWatcher.observe(searchBarToWatch);
$searchBarWrappers.forEach(($wrapper) => {
const $searchForm = $wrapper.querySelector('.search-form');
@@ -737,7 +774,8 @@ function initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper
$searchForm.addEventListener('submit', async (e) => {
e.preventDefault();
- await redirectSearch($searchBar);
+ logSearch(e.currentTarget);
+ await redirectSearch($searchBar, props);
});
$clear.addEventListener('click', () => {
@@ -808,14 +846,6 @@ function initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper
}
}, 500);
}, { passive: true });
-
- $stickySearchBarWrapper.addEventListener('mouseleave', () => {
- if (!$stickySearchBar || $stickySearchBar !== document.activeElement) {
- $stickySearchBarWrapper.classList.remove('ready');
- $stickySearchBarWrapper.classList.add('collapsed');
- resetTaskDropdowns($section);
- }
- }, { passive: true });
}
function updateLottieStatus($section) {
@@ -833,116 +863,117 @@ function updateLottieStatus($section) {
}
}
-function decorateCategoryList($block, $section, placeholders) {
- const params = new Proxy(new URLSearchParams(window.location.search), {
- get: (searchParams, prop) => searchParams.get(prop),
- });
+async function decorateCategoryList(block, section, placeholders, props) {
+ const $blockWrapper = block.closest('.template-list-wrapper');
+ const $mobileDrawerWrapper = section.querySelector('.filter-drawer-mobile');
+ const $inWrapper = section.querySelector('.filter-drawer-mobile-inner-wrapper');
+ const categories = JSON.parse(placeholders['task-categories']);
+ const categoryIcons = placeholders['task-category-icons'].replace(/\s/g, '').split(',');
+ const $categoriesDesktopWrapper = createTag('div', { class: 'category-list-wrapper' });
+ const $categoriesToggleWrapper = createTag('div', { class: 'category-list-toggle-wrapper' });
+ const $desktopCategoriesToggleWrapper = createTag('div', { class: 'category-list-toggle-wrapper' });
+ const $categoriesToggle = createTag('span', { class: 'category-list-toggle' });
+ const desktopCategoriesToggle = getIconElement('drop-down-arrow');
+ const categoriesListHeading = createTag('div', { class: 'category-list-heading' });
+ const $categories = createTag('ul', { class: 'category-list' });
+
+ categoriesListHeading.append(getIconElement('template-search'), placeholders['jump-to-category']);
+ $categoriesToggle.textContent = placeholders['jump-to-category'];
+ const allTemplatesMetadata = await fetchAllTemplatesMetadata();
- if (params.tasks) {
- const locale = getLocale(window.location);
- const $blockWrapper = $block.closest('.template-list-wrapper');
- const $mobileDrawerWrapper = $section.querySelector('.filter-drawer-mobile');
- const $inWrapper = $section.querySelector('.filter-drawer-mobile-inner-wrapper');
- const categories = JSON.parse(placeholders['task-categories']);
- const categoryIcons = placeholders['task-category-icons'].replace(/\s/g, '').split(',');
- const $categoriesDesktopWrapper = createTag('div', { class: 'category-list-wrapper' });
- const $categoriesToggleWrapper = createTag('div', { class: 'category-list-toggle-wrapper' });
- const $categoriesToggle = createTag('span', { class: 'category-list-toggle' });
- const $categories = createTag('ul', { class: 'category-list' });
-
- $categoriesToggle.textContent = placeholders['jump-to-category'];
-
- $categoriesToggleWrapper.append($categoriesToggle);
- $categoriesDesktopWrapper.append($categoriesToggleWrapper, $categories);
-
- Object.entries(categories).forEach((category, index) => {
- const format = `${props.placeholderFormat[0]}:${props.placeholderFormat[1]}`;
- const targetTasks = category[1];
- const currentTasks = trimFormattedFilterText(props.filters.tasks) ? trimFormattedFilterText(props.filters.tasks) : "''";
- const currentTopic = trimFormattedFilterText(props.filters.topics);
-
- const $listItem = createTag('li');
- if (category[1] === currentTasks) {
- $listItem.classList.add('active');
- }
+ Object.entries(categories).forEach((category, index) => {
+ const format = `${props.placeholderFormat[0]}:${props.placeholderFormat[1]}`;
+ const targetTasks = category[1];
+ const currentTasks = trimFormattedFilterText(props.filters.tasks) ? trimFormattedFilterText(props.filters.tasks) : "''";
+ const currentTopic = trimFormattedFilterText(props.filters.topics);
- let icon;
- if (categoryIcons[index] && categoryIcons[index] !== '') {
- icon = categoryIcons[index];
- } else {
- icon = 'template-static';
- }
+ const $listItem = createTag('li');
+ if (category[1] === currentTasks) {
+ $listItem.classList.add('active');
+ }
- const iconElement = getIconElement(icon);
- const urlPrefix = locale === 'us' ? '' : `/${locale}`;
- const $a = createTag('a', {
- 'data-tasks': targetTasks,
- href: `${urlPrefix}/express/templates/search?tasks=${targetTasks}&phformat=${format}&topics=${currentTopic || "''"}`,
- });
- [$a.textContent] = category;
+ let icon;
+ if (categoryIcons[index] && categoryIcons[index] !== '') {
+ icon = categoryIcons[index];
+ } else {
+ icon = 'template-static';
+ }
- $a.prepend(iconElement);
- $listItem.append($a);
- $categories.append($listItem);
+ const iconElement = getIconElement(icon);
+ const redirectUrl = getRedirectUrl(targetTasks, currentTopic, format, allTemplatesMetadata);
+ const $a = createTag('a', {
+ 'data-tasks': targetTasks,
+ href: redirectUrl,
});
+ [$a.textContent] = category;
- const $categoriesMobileWrapper = $categoriesDesktopWrapper.cloneNode({ deep: true });
+ $a.prepend(iconElement);
+ $listItem.append($a);
+ $categories.append($listItem);
+ });
- const $lottieArrows = createTag('a', { class: 'lottie-wrapper' });
- $mobileDrawerWrapper.append($lottieArrows);
- $inWrapper.append($categoriesMobileWrapper);
- $lottieArrows.innerHTML = getLottie('purple-arrows', '/express/icons/purple-arrows.json');
- lazyLoadLottiePlayer();
+ $categoriesToggleWrapper.append($categoriesToggle);
+ $categoriesDesktopWrapper.append($categories);
+ const $categoriesMobileWrapper = $categoriesDesktopWrapper.cloneNode({ deep: true });
+ $categoriesMobileWrapper.prepend($categoriesToggleWrapper);
- $categoriesDesktopWrapper.classList.add('desktop-only');
+ $desktopCategoriesToggleWrapper.append(desktopCategoriesToggle);
+ $categoriesDesktopWrapper.prepend($desktopCategoriesToggleWrapper, categoriesListHeading);
- if ($blockWrapper) {
- $blockWrapper.prepend($categoriesDesktopWrapper);
- $blockWrapper.classList.add('with-categories-list');
- }
+ const $lottieArrows = createTag('a', { class: 'lottie-wrapper' });
+ $mobileDrawerWrapper.append($lottieArrows);
+ $inWrapper.append($categoriesMobileWrapper);
+ $lottieArrows.innerHTML = getLottie('purple-arrows', '/express/icons/purple-arrows.json');
+ lazyLoadLottiePlayer();
- const $toggleButton = $categoriesMobileWrapper.querySelector('.category-list-toggle-wrapper');
- $toggleButton.append(getIconElement('drop-down-arrow'));
- $toggleButton.addEventListener('click', () => {
- const $listWrapper = $toggleButton.parentElement;
- $toggleButton.classList.toggle('collapsed');
- if ($toggleButton.classList.contains('collapsed')) {
- if ($listWrapper.classList.contains('desktop-only')) {
- $listWrapper.classList.add('collapsed');
- $listWrapper.style.maxHeight = '40px';
- } else {
- $listWrapper.classList.add('collapsed');
- $listWrapper.style.maxHeight = '24px';
- }
+ $categoriesDesktopWrapper.classList.add('desktop-only');
+
+ if ($blockWrapper) {
+ $blockWrapper.prepend($categoriesDesktopWrapper);
+ $blockWrapper.classList.add('with-categories-list');
+ }
+
+ const $toggleButton = $categoriesMobileWrapper.querySelector('.category-list-toggle-wrapper');
+ $toggleButton.append(getIconElement('drop-down-arrow'));
+ $toggleButton.addEventListener('click', () => {
+ const $listWrapper = $toggleButton.parentElement;
+ $toggleButton.classList.toggle('collapsed');
+ if ($toggleButton.classList.contains('collapsed')) {
+ if ($listWrapper.classList.contains('desktop-only')) {
+ $listWrapper.classList.add('collapsed');
+ $listWrapper.style.maxHeight = '40px';
} else {
- $listWrapper.classList.remove('collapsed');
- $listWrapper.style.maxHeight = '1000px';
+ $listWrapper.classList.add('collapsed');
+ $listWrapper.style.maxHeight = '24px';
}
+ } else {
+ $listWrapper.classList.remove('collapsed');
+ $listWrapper.style.maxHeight = '1000px';
+ }
- setTimeout(() => {
- if (!$listWrapper.classList.contains('desktop-only')) {
- updateLottieStatus($section);
- }
- }, 510);
- }, { passive: true });
+ setTimeout(() => {
+ if (!$listWrapper.classList.contains('desktop-only')) {
+ updateLottieStatus(section);
+ }
+ }, 510);
+ }, { passive: true });
- $lottieArrows.addEventListener('click', () => {
- $inWrapper.scrollBy({
- top: 300,
- behavior: 'smooth',
- });
- }, { passive: true });
+ $lottieArrows.addEventListener('click', () => {
+ $inWrapper.scrollBy({
+ top: 300,
+ behavior: 'smooth',
+ });
+ }, { passive: true });
- $inWrapper.addEventListener('scroll', () => {
- updateLottieStatus($section);
- }, { passive: true });
- }
+ $inWrapper.addEventListener('scroll', () => {
+ updateLottieStatus(section);
+ }, { passive: true });
}
-async function decorateSearchFunctions($toolBar, $section, placeholders) {
+async function decorateSearchFunctions($toolBar, $section, placeholders, props) {
const $inBlockLocation = $toolBar.querySelector('.wrapper-content-search');
- const $inSectionLocation = $section.querySelector('.link-list-wrapper');
- const $searchBarWrapper = createTag('div', { class: 'search-bar-wrapper' });
+ const $linkListLocation = document.querySelector('.link-list-fullwidth-container');
+ const $searchBarWrapper = createTag('div', { class: 'template-list-search-bar-wrapper' });
const $searchForm = createTag('form', { class: 'search-form' });
const $searchBar = createTag('input', {
class: 'search-bar',
@@ -994,9 +1025,15 @@ async function decorateSearchFunctions($toolBar, $section, placeholders) {
$stickySearchBarWrapper.classList.add('sticky-search-bar');
$stickySearchBarWrapper.classList.add('collapsed');
$inBlockLocation.append($stickySearchBarWrapper);
- $inSectionLocation.insertAdjacentElement('beforebegin', $searchBarWrapper);
-
- initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper);
+ if ($linkListLocation) {
+ const linkListWrapper = $linkListLocation.querySelector('.link-list-wrapper');
+ if (linkListWrapper) {
+ linkListWrapper.before($searchBarWrapper);
+ } else {
+ $linkListLocation.prepend($searchBarWrapper);
+ }
+ }
+ initSearchFunction($toolBar, $stickySearchBarWrapper, $searchBarWrapper, props);
}
function closeDrawer($toolBar) {
@@ -1015,7 +1052,7 @@ function closeDrawer($toolBar) {
}, 500);
}
-function updateOptionsStatus($block, $toolBar) {
+function updateOptionsStatus($block, $toolBar, props) {
const $wrappers = $toolBar.querySelectorAll('.function-wrapper');
$wrappers.forEach(($wrapper) => {
@@ -1054,7 +1091,7 @@ function updateOptionsStatus($block, $toolBar) {
});
}
-function initDrawer($block, $section, $toolBar) {
+function initDrawer($block, $section, $toolBar, props) {
const $filterButton = $toolBar.querySelector('.filter-button-mobile-wrapper');
const $drawerBackground = $toolBar.querySelector('.drawer-background');
const $drawer = $toolBar.querySelector('.filter-drawer-mobile');
@@ -1090,7 +1127,7 @@ function initDrawer($block, $section, $toolBar) {
$element.addEventListener('click', () => {
props.filters = { ...currentFilters };
closeDrawer($toolBar);
- updateOptionsStatus($block, $toolBar);
+ updateOptionsStatus($block, $toolBar, props);
}, { passive: true });
});
@@ -1122,7 +1159,7 @@ function initDrawer($block, $section, $toolBar) {
$drawer.classList.add('hidden');
}
-function updateQueryURL(functionWrapper, option) {
+function updateQueryURL(functionWrapper, option, props) {
const paramType = functionWrapper.dataset.param;
const paramValue = option.dataset.value;
@@ -1145,7 +1182,7 @@ function updateQueryURL(functionWrapper, option) {
}
}
-function updateLoadMoreButton($block, $loadMore) {
+function updateLoadMoreButton($block, $loadMore, props) {
if (props.start === '') {
$loadMore.style.display = 'none';
} else {
@@ -1153,12 +1190,12 @@ function updateLoadMoreButton($block, $loadMore) {
}
}
-async function decorateNewTemplates($block, options = { reDrawMasonry: false }) {
- const newTemplates = await processResponse();
+async function decorateNewTemplates($block, props, options = { reDrawMasonry: false }) {
+ const newTemplates = await processResponse(props);
const $loadMore = $block.parentElement.querySelector('.load-more');
props.templates = props.templates.concat(newTemplates);
- populateTemplates($block, newTemplates);
+ populateTemplates($block, newTemplates, props);
const newCells = Array.from($block.querySelectorAll('.template:not(.appear)'));
@@ -1170,11 +1207,11 @@ async function decorateNewTemplates($block, options = { reDrawMasonry: false })
props.masonry.draw(newCells);
if ($loadMore) {
- updateLoadMoreButton($block, $loadMore);
+ updateLoadMoreButton($block, $loadMore, props);
}
}
-async function redrawTemplates($block, $toolBar) {
+async function redrawTemplates($block, $toolBar, props) {
const $heading = $toolBar.querySelector('h2');
const lang = getLanguage(getLocale(window.location));
const currentTotal = props.total.toLocaleString(lang);
@@ -1184,9 +1221,9 @@ async function redrawTemplates($block, $toolBar) {
$card.remove();
});
- await decorateNewTemplates($block, { reDrawMasonry: true }).then(() => {
+ await decorateNewTemplates($block, props, { reDrawMasonry: true }).then(() => {
$heading.textContent = $heading.textContent.replace(`${currentTotal}`, props.total.toLocaleString(lang));
- updateOptionsStatus($block, $toolBar);
+ updateOptionsStatus($block, $toolBar, props);
if ($block.querySelectorAll('.template').length <= 0) {
const $viewButtons = $toolBar.querySelectorAll('.view-toggle-button');
$viewButtons.forEach(($button) => {
@@ -1199,7 +1236,7 @@ async function redrawTemplates($block, $toolBar) {
});
}
-async function toggleAnimatedText($block, $toolBar) {
+async function toggleAnimatedText($block, $toolBar, props) {
const section = $block.closest('.section.template-list-fullwidth-apipowered-container');
const $toolbarWrapper = $toolBar.parentElement;
@@ -1219,7 +1256,7 @@ async function toggleAnimatedText($block, $toolBar) {
}
}
-function initFilterSort($block, $toolBar) {
+function initFilterSort($block, $toolBar, props) {
const $buttons = $toolBar.querySelectorAll('.button-wrapper');
const $applyFilterButton = $toolBar.querySelector('.apply-filter-button');
@@ -1261,15 +1298,15 @@ function initFilterSort($block, $toolBar) {
});
$option.classList.add('active');
- updateQueryURL($wrapper, $option);
+ updateQueryURL($wrapper, $option, props);
updateFilterIcon($block);
if (!$optionsList.classList.contains('in-drawer')) {
- await toggleAnimatedText($block, $toolBar);
+ await toggleAnimatedText($block, $toolBar, props);
}
if (!$button.classList.contains('in-drawer')) {
- await redrawTemplates($block, $toolBar);
+ await redrawTemplates($block, $toolBar, props);
}
};
@@ -1300,14 +1337,14 @@ function initFilterSort($block, $toolBar) {
if ($applyFilterButton) {
$applyFilterButton.addEventListener('click', async (e) => {
e.preventDefault();
- await redrawTemplates($block, $toolBar);
+ await redrawTemplates($block, $toolBar, props);
closeDrawer($toolBar);
- await toggleAnimatedText($block, $toolBar);
+ await toggleAnimatedText($block, $toolBar, props);
});
}
// sync current filter & sorting method with toolbar current options
- updateOptionsStatus($block, $toolBar);
+ updateOptionsStatus($block, $toolBar, props);
updateFilterIcon($block);
}
}
@@ -1355,7 +1392,7 @@ function getPlaceholderWidth($block) {
return width;
}
-function toggleMasonryView($block, $button, $toggleButtons) {
+function toggleMasonryView($block, $button, $toggleButtons, props) {
const $templatesToView = $block.querySelectorAll('.template:not(.placeholder)');
const $blockWrapper = $block.closest('.template-list-wrapper');
if (!$button.classList.contains('active') && $templatesToView.length > 0) {
@@ -1375,7 +1412,7 @@ function toggleMasonryView($block, $button, $toggleButtons) {
$block.classList.add(`${$button.dataset.view}-view`);
$blockWrapper.classList.add(`${$button.dataset.view}-view`);
- props.masonry.draw();
+ props.masonry?.draw();
} else {
$button.classList.remove('active');
['sm-view', 'md-view', 'lg-view'].forEach((className) => {
@@ -1383,14 +1420,14 @@ function toggleMasonryView($block, $button, $toggleButtons) {
$blockWrapper.classList.remove(className);
});
- props.masonry.draw();
+ props.masonry?.draw();
}
const placeholder = $block.querySelector('.template.placeholder');
const ratios = props.placeholderFormat;
const width = getPlaceholderWidth($block);
- if (ratios[1]) {
+ if (ratios?.length === 2) {
const height = (ratios[1] / ratios[0]) * width;
placeholder.style = `height: ${height - 21}px`;
if (width / height > 1.3) {
@@ -1399,20 +1436,28 @@ function toggleMasonryView($block, $button, $toggleButtons) {
}
}
-function initViewToggle($block, $toolBar) {
+function initViewToggle($block, $toolBar, props) {
const $toggleButtons = $toolBar.querySelectorAll('.view-toggle-button ');
$toggleButtons.forEach(($button, index) => {
if (index === 0) {
- toggleMasonryView($block, $button, $toggleButtons);
+ toggleMasonryView($block, $button, $toggleButtons, props);
}
$button.addEventListener('click', () => {
- toggleMasonryView($block, $button, $toggleButtons);
+ toggleMasonryView($block, $button, $toggleButtons, props);
}, { passive: true });
});
}
+async function decorateBreadcrumbs(block) {
+ const parent = block.closest('.section');
+ // breadcrumbs are desktop-only
+ if (document.body.dataset.device !== 'desktop') return;
+ const breadcrumbs = await getBreadcrumbs();
+ if (breadcrumbs) parent.prepend(breadcrumbs);
+}
+
function initToolbarShadow($block, $toolbar) {
const $toolbarWrapper = $toolbar.parentElement;
document.addEventListener('scroll', () => {
@@ -1424,7 +1469,7 @@ function initToolbarShadow($block, $toolbar) {
});
}
-function decorateToolbar($block, $section, placeholders) {
+function decorateToolbar($block, $section, placeholders, props) {
const $toolBar = $section.querySelector('.api-templates-toolbar');
if ($toolBar) {
@@ -1448,19 +1493,19 @@ function decorateToolbar($block, $section, placeholders) {
$toolBar.append(toolBarFirstWrapper, functionsWrapper, $functions.mobile);
- decorateSearchFunctions($toolBar, $section, placeholders);
- initDrawer($block, $section, $toolBar);
- initFilterSort($block, $toolBar);
- initViewToggle($block, $toolBar);
+ decorateSearchFunctions($toolBar, $section, placeholders, props);
+ initDrawer($block, $section, $toolBar, props);
+ initFilterSort($block, $toolBar, props);
+ initViewToggle($block, $toolBar, props);
initToolbarShadow($block, $toolBar);
}
}
-export async function decorateTemplateList($block) {
+export async function decorateTemplateList($block, props) {
const locale = getLocale(window.location);
- const placeholders = await fetchPlaceholders().then((result) => result);
+ const placeholders = await fetchPlaceholders();
if ($block.classList.contains('apipowered')) {
- await readRowsFromBlock($block);
+ await readRowsFromBlock($block, props);
const $parent = $block.closest('.section');
if ($parent) {
@@ -1538,7 +1583,7 @@ export async function decorateTemplateList($block) {
} else if (props.authoringError) {
$sectionHeading.textContent = props.heading;
} else {
- $sectionHeading.textContent = await populateHeadingPlaceholder(locale) || '';
+ $sectionHeading.textContent = await populateHeadingPlaceholder(locale, props) || '';
}
}
@@ -1546,6 +1591,12 @@ export async function decorateTemplateList($block) {
$toolBar.classList.remove('default-content-wrapper');
$templateListWrapper.before($toolBarWrapper);
+ if (props.fallbackMsg) {
+ $templateListWrapper.classList.add('with-fallback-msg');
+ const fallbackMsgWrapper = createTag('div', { class: 'template-list-fallback-msg-wrapper' });
+ fallbackMsgWrapper.textContent = props.fallbackMsg;
+ $templateListWrapper.before(fallbackMsgWrapper);
+ }
$toolBarWrapper.append($toolBar);
$toolBar.append($contentWrapper, $functionsWrapper);
$contentWrapper.append($sectionHeading);
@@ -1555,15 +1606,13 @@ export async function decorateTemplateList($block) {
}
}
- const $linkList = $parent.querySelector('.link-list-wrapper');
- if ($linkList
- && $linkList.previousElementSibling.classList.contains('hero-animation-wrapper')
- && placeholders['template-filter-premium']) {
- document.addEventListener('linkspopulated', (e) => {
- if (e.detail.length > 0 && e.detail[0].parentElement.classList.contains('template-list')) {
- decorateToolbar($block, $parent, placeholders);
- decorateCategoryList($block, $parent, placeholders);
- appendCategoryTemplatesCount($parent);
+ if (placeholders['template-filter-premium']) {
+ document.addEventListener('linkspopulated', async (e) => {
+ // desktop/mobile fires the same event
+ if ($parent.contains(e.detail[0])) {
+ decorateToolbar($block, $parent, placeholders, props);
+ await decorateCategoryList($block, $parent, placeholders, props);
+ appendCategoryTemplatesCount($parent, props);
}
});
}
@@ -1711,7 +1760,7 @@ export async function decorateTemplateList($block) {
//
// make copy of children to avoid modifying list while looping
- populateTemplates($block, templates);
+ populateTemplates($block, templates, props);
if ($block.classList.contains('spreadsheet-powered')
&& !$block.classList.contains('apipowered')
@@ -1753,9 +1802,8 @@ export async function decorateTemplateList($block) {
document.dispatchEvent(linksPopulated);
}
-async function decorateLoadMoreButton($block) {
- const placeholders = await fetchPlaceholders()
- .then((result) => result);
+async function decorateLoadMoreButton($block, props) {
+ const placeholders = await fetchPlaceholders();
const $loadMoreDiv = createTag('div', { class: 'load-more' });
const $loadMoreButton = createTag('button', { class: 'load-more-button' });
const $loadMoreText = createTag('p', { class: 'load-more-text' });
@@ -1767,7 +1815,7 @@ async function decorateLoadMoreButton($block) {
$loadMoreButton.addEventListener('click', async () => {
$loadMoreButton.classList.add('disabled');
const scrollPosition = window.scrollY;
- await decorateNewTemplates($block);
+ await decorateNewTemplates($block, props);
window.scrollTo({
top: scrollPosition,
left: 0,
@@ -1779,11 +1827,11 @@ async function decorateLoadMoreButton($block) {
return $loadMoreDiv;
}
-async function decorateTailButton($block) {
+async function decorateTailButton($block, props) {
const $carouselPlatform = $block.querySelector('.carousel-platform');
if ($block.classList.contains('spreadsheet-powered')) {
- const placeholders = await fetchPlaceholders().then((result) => result);
+ const placeholders = await fetchPlaceholders();
if (placeholders['relevant-rows-view-all'] && (props.viewAllLink || placeholders['relevant-rows-view-all-link'])) {
props.tailButton = createTag('a', { class: 'button accent tail-cta' });
@@ -1798,7 +1846,7 @@ async function decorateTailButton($block) {
}
}
-function cacheCreatedTemplate($block) {
+function cacheCreatedTemplate($block, props) {
const lastRow = $block.children[$block.children.length - 1];
if (lastRow && lastRow.querySelector(':scope > div:first-of-type > img[src*=".svg"], :scope > div:first-of-type > svg')) {
props.templates.push(lastRow.cloneNode(true));
@@ -1824,7 +1872,7 @@ function addBackgroundAnimation($block, animationUrl) {
}
}
-async function replaceRRTemplateList($block) {
+async function replaceRRTemplateList($block, props) {
const placeholders = await fetchPlaceholders();
const relevantRowsData = await fetchRelevantRows(window.location.pathname);
props.limit = parseInt(placeholders['relevant-rows-templates-limit'], 10) || 10;
@@ -1879,16 +1927,57 @@ async function replaceRRTemplateList($block) {
}
}
+function constructProps() {
+ const smScreen = window.matchMedia('(max-width: 900px)');
+ const mdScreen = window.matchMedia('(min-width: 901px) and (max-width: 1200px)');
+ const bgScreen = window.matchMedia('(max-width: 1440px)');
+ const ratioSeparator = getMetadata('placeholder-format').includes(':') ? ':' : 'x';
+ const ratioFromMetadata = getMetadata('placeholder-format')
+ .split(ratioSeparator)
+ .map((str) => parseInt(str, 10));
+
+ return {
+ templates: [],
+ filters: {
+ locales: getMetadata('locales') || '(en)',
+ tasks: getMetadata('tasks') || '',
+ topics: getMetadata('topics') || '',
+ premium: getMetadata('premium') || '',
+ animated: getMetadata('animated') || '',
+ },
+ tailButton: '',
+ // eslint-disable-next-line no-nested-ternary
+ limit: smScreen.matches ? 20 : mdScreen.matches ? 30 : bgScreen.matches ? 40 : 70,
+ total: 0,
+ start: '',
+ sort: '-_score,-remixCount',
+ masonry: undefined,
+ authoringError: false,
+ headingTitle: null,
+ headingSlug: null,
+ viewAllLink: null,
+ placeholderFormat: ratioFromMetadata,
+ renditionParams: {
+ format: 'jpg',
+ dimension: 'width',
+ size: 151,
+ },
+ };
+}
+
export default async function decorate($block) {
+ const props = constructProps();
if ($block.classList.contains('spreadsheet-powered')) {
- await replaceRRTemplateList($block);
+ await replaceRRTemplateList($block, props);
}
if ($block.classList.contains('apipowered') && !$block.classList.contains('holiday')) {
- cacheCreatedTemplate($block);
+ cacheCreatedTemplate($block, props);
}
- await decorateTemplateList($block);
+ await decorateBreadcrumbs($block);
+
+ await decorateTemplateList($block, props);
if ($block.classList.contains('horizontal')) {
const requireInfiniteScroll = !$block.classList.contains('mini') && !$block.classList.contains('collaboration');
@@ -1898,15 +1987,15 @@ export default async function decorate($block) {
}
if ($block.classList.contains('apipowered') && !$block.classList.contains('holiday') && !$block.classList.contains('mini')) {
- const $loadMore = await decorateLoadMoreButton($block);
+ const $loadMore = await decorateLoadMoreButton($block, props);
if ($loadMore) {
- updateLoadMoreButton($block, $loadMore);
+ updateLoadMoreButton($block, $loadMore, props);
}
}
if ($block.classList.contains('mini') || $block.classList.contains('apipowered')) {
- await decorateTailButton($block);
+ await decorateTailButton($block, props);
}
if ($block.classList.contains('holiday') && props.backgroundAnimation) {
diff --git a/express/icons/template-free-accent.svg b/express/icons/template-free-accent.svg
new file mode 100644
index 000000000..86b10c709
--- /dev/null
+++ b/express/icons/template-free-accent.svg
@@ -0,0 +1,9 @@
+
diff --git a/express/icons/template-search.svg b/express/icons/template-search.svg
new file mode 100644
index 000000000..aab15ce2f
--- /dev/null
+++ b/express/icons/template-search.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/express/scripts/all-templates-metadata.js b/express/scripts/all-templates-metadata.js
new file mode 100644
index 000000000..dc5186f24
--- /dev/null
+++ b/express/scripts/all-templates-metadata.js
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import { getHelixEnv, getLocale } from './scripts.js';
+import { memoize } from './utils.js';
+
+const memoizedFetchUrl = memoize((url) => fetch(url).then((r) => (r.ok ? r.json() : null)), {
+ key: (q) => q,
+ ttl: 1000 * 60 * 60 * 24,
+});
+
+let allTemplatesMetadata;
+
+export default async function fetchAllTemplatesMetadata() {
+ const locale = getLocale(window.location);
+ const urlPrefix = locale === 'us' ? '' : `/${locale}`;
+
+ if (!allTemplatesMetadata) {
+ try {
+ const env = getHelixEnv();
+ const dev = new URLSearchParams(window.location.search).get('dev');
+ let sheet;
+
+ if (['yes', 'true', 'on'].includes(dev) && env?.name === 'stage') {
+ sheet = '/templates-dev.json?sheet=seo-templates&limit=100000';
+ } else {
+ sheet = `${urlPrefix}/express/templates/default/metadata.json?limit=100000`;
+ }
+
+ let resp = await memoizedFetchUrl(sheet);
+ allTemplatesMetadata = resp?.data;
+
+ // TODO: remove the > 1 logic after publishing of the split metadata sheet
+ if (!(allTemplatesMetadata && allTemplatesMetadata.length > 1)) {
+ resp = await memoizedFetchUrl('/express/templates/content.json?sheet=seo-templates&limit=100000');
+ allTemplatesMetadata = resp?.data?.map((p) => ({
+ ...p,
+ // TODO: backward compatibility. Remove when we move away from helix-seo-templates
+ url: p.path,
+ title: p.metadataTitle,
+ description: p.metadataDescription,
+ 'short-title': p.shortTitle,
+ ckgid: p.ckgID,
+ 'hero-title': p.heroAnimationTitle,
+ 'hero-text': p.heroAnimationText,
+ locales: p.templateLocale,
+ premium: p.templatePremium,
+ animated: p.templateAnimated,
+ tasks: p.templateTasks,
+ topics: p.templateTopics,
+ 'placeholder-format': p.placeholderFormat,
+ 'create-link': p.createLink,
+ 'create-text': p.createText,
+ 'top-templates': p.topTemplates,
+ 'top-templates-text': p.topTemplatesText,
+ })) || [];
+ }
+ } catch (err) {
+ allTemplatesMetadata = [];
+ }
+ }
+ return allTemplatesMetadata;
+}
diff --git a/express/scripts/api-v3-controller.js b/express/scripts/api-v3-controller.js
index 60a7c6afe..6eca0a3e0 100644
--- a/express/scripts/api-v3-controller.js
+++ b/express/scripts/api-v3-controller.js
@@ -10,11 +10,11 @@
* governing permissions and limitations under the License.
*/
-import { getHelixEnv, getLocale } from './scripts.js';
+import { getHelixEnv, getLocale, getMetadata } from './scripts.js';
const endpoints = {
dev: {
- cdn: '',
+ cdn: 'https://uss-templates-dev.adobe.io/uss/v3/query',
url: 'https://uss-templates-dev.adobe.io/uss/v3/query',
token: 'cd1823ed-0104-492f-ba91-25f4195d5f6c',
},
@@ -69,8 +69,8 @@ export default async function getData(env = '', data = {}) {
}
}
-export async function fetchLinkListFromCKGApi(pageData) {
- if (pageData.ckgID) {
+export async function fetchLinkListFromCKGApi() {
+ if (getMetadata('ckgid')) {
const dataRaw = {
experienceId: 'templates-browse-v1',
locale: 'en_US',
@@ -87,7 +87,7 @@ export async function fetchLinkListFromCKGApi(pageData) {
filters: [
{
categories: [
- pageData.ckgID,
+ getMetadata('ckgid'),
],
},
],
@@ -102,7 +102,7 @@ export async function fetchLinkListFromCKGApi(pageData) {
};
const env = getHelixEnv();
- const result = await getData(env.name, dataRaw).then((data) => data);
+ const result = await getData(env.name, dataRaw);
if (result.status.httpCode === 200) {
return result;
}
diff --git a/express/scripts/ckg-link-list.js b/express/scripts/ckg-link-list.js
new file mode 100644
index 000000000..0fdfe4209
--- /dev/null
+++ b/express/scripts/ckg-link-list.js
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2022 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+import {
+ titleCase,
+ getLocale,
+ getMetadata,
+} from './scripts.js';
+
+import {
+ fetchLinkListFromCKGApi,
+ getPillWordsMapping,
+} from './api-v3-controller.js';
+
+import { memoize } from './utils.js';
+import fetchAllTemplatesMetadata from './all-templates-metadata.js';
+
+async function fetchLinkList() {
+ if (!window.linkLists) {
+ window.linkLists = {};
+ if (!window.linkLists.ckgData) {
+ const response = await fetchLinkListFromCKGApi();
+ // catch data from CKG API, if empty, use top priority categories sheet
+ if (response && response.queryResults[0].facets) {
+ window.linkLists.ckgData = response.queryResults[0].facets[0].buckets.map((ckgItem) => {
+ let formattedTasks;
+ if (getMetadata('template-search-page') === 'Y') {
+ const params = new Proxy(new URLSearchParams(window.location.search), {
+ get: (searchParams, prop) => searchParams.get(prop),
+ });
+ formattedTasks = titleCase(params.tasks).replace(/[$@%"]/g, '');
+ } else {
+ formattedTasks = titleCase(getMetadata('tasks')).replace(/[$@%"]/g, '');
+ }
+
+ return {
+ parent: formattedTasks,
+ 'child-siblings': `${titleCase(ckgItem.displayValue)} ${formattedTasks}`,
+ ckgID: ckgItem.canonicalName,
+ displayValue: ckgItem.displayValue,
+ };
+ });
+ }
+ }
+
+ if (!window.linkLists.sheetData) {
+ const resp = await fetch('/express/templates/top-priority-categories.json');
+ window.linkLists.sheetData = resp.ok ? (await resp.json()).data : [];
+ }
+ }
+}
+
+function matchCKGResult(ckgData, pageData) {
+ const ckgMatch = pageData.ckgID === ckgData.ckgID;
+ const pageDataTasks = pageData.tasks ?? pageData.templateTasks;
+ const taskMatch = ckgData.tasks?.toLowerCase() === pageDataTasks?.toLowerCase();
+ const currentLocale = getLocale(window.location);
+ const pageLocale = pageData.url.split('/')[1] === 'express' ? 'us' : pageData.url.split('/')[1];
+ const sameLocale = currentLocale === pageLocale;
+
+ return sameLocale && ckgMatch && taskMatch;
+}
+
+function replaceLinkPill(linkPill, data) {
+ const clone = linkPill.cloneNode(true);
+ if (data) {
+ clone.innerHTML = clone.innerHTML.replace('/express/templates/default', data.url);
+ clone.innerHTML = clone.innerHTML.replaceAll('Default', data.altShortTitle || data['short-title']);
+ }
+ return clone;
+}
+
+async function updateSEOLinkList(container, linkPill, list) {
+ const templatePages = await fetchAllTemplatesMetadata();
+ container.innerHTML = '';
+
+ if (list && templatePages) {
+ list.forEach((d) => {
+ const currentLocale = getLocale(window.location);
+ const templatePageData = templatePages.find((p) => {
+ const targetLocale = /^[a-z]{2}$/.test(p.url.split('/')[1]) ? p.url.split('/')[1] : 'us';
+ const isLive = p.live === 'Y';
+ const titleMatch = p['short-title'].toLowerCase() === d.childSibling.toLowerCase();
+ const localeMatch = currentLocale === targetLocale;
+
+ return isLive && titleMatch && localeMatch;
+ });
+
+ if (templatePageData) {
+ const clone = replaceLinkPill(linkPill, templatePageData);
+ container.append(clone);
+ }
+ });
+ }
+}
+
+function formatLinkPillText(linkPillData) {
+ const digestedDisplayValue = titleCase(linkPillData.displayValue.replace(/-/g, ' '));
+ const digestedChildSibling = titleCase(linkPillData.childSibling.replace(/-/g, ' '));
+ const topics = getMetadata('topics') !== '" "' ? `${getMetadata('topics').replace(/[$@%"]/g, '').replace(/-/g, ' ')}` : '';
+
+ const displayTopics = topics && linkPillData.childSibling.indexOf(titleCase(topics)) < 0 ? titleCase(topics) : '';
+ let displayText;
+
+ if (getMetadata('tasks')) {
+ displayText = `${displayTopics} ${digestedDisplayValue} ${digestedChildSibling}`
+ .split(' ')
+ .filter((item, i, allItems) => i === allItems.indexOf(item))
+ .join(' ').trim();
+ } else {
+ displayText = `${digestedDisplayValue} ${digestedChildSibling} ${displayTopics}`
+ .split(' ')
+ .filter((item, i, allItems) => i === allItems.indexOf(item))
+ .join(' ').trim();
+ }
+
+ return displayText;
+}
+
+const memoizedGetPillWordsMapping = memoize(getPillWordsMapping, { ttl: 1000 * 60 * 60 * 24 });
+
+async function updateLinkList(container, linkPill, list) {
+ const templatePages = await fetchAllTemplatesMetadata();
+ const pillsMapping = await memoizedGetPillWordsMapping();
+ const pageLinks = [];
+ const searchLinks = [];
+ container.innerHTML = '';
+
+ if (list && templatePages) {
+ list.forEach((d) => {
+ const topics = getMetadata('topics') !== '" "' ? `${getMetadata('topics').replace(/[$@%"]/g, '')}` : '';
+ const templatePageData = templatePages.find((p) => p.live === 'Y' && matchCKGResult(d, p));
+ const topicsQuery = `${topics ?? topics} ${d.displayValue}`.split(' ')
+ .filter((item, i, allItems) => i === allItems.indexOf(item))
+ .join(' ').trim();
+ let displayText = formatLinkPillText(d);
+
+ const locale = getLocale(window.location);
+ const urlPrefix = locale === 'us' ? '' : `/${locale}`;
+ const localeColumnString = locale === 'us' ? 'EN' : locale.toUpperCase();
+ let hideUntranslatedPill = false;
+
+ if (pillsMapping) {
+ const alternateText = pillsMapping.find((row) => getMetadata('url') === `${urlPrefix}${row['Express SEO URL']}` && d.ckgID === row['CKG Pill ID']);
+
+ if (alternateText && alternateText[`${localeColumnString}`]) {
+ displayText = alternateText[`${localeColumnString}`];
+ if (templatePageData) {
+ templatePageData.altShortTitle = displayText;
+ }
+ }
+
+ hideUntranslatedPill = displayText && locale !== 'us';
+ }
+
+ if (templatePageData) {
+ const clone = replaceLinkPill(linkPill, templatePageData);
+ pageLinks.push(clone);
+ } else if (d.ckgID && !hideUntranslatedPill) {
+ const currentTasks = getMetadata('tasks') ? getMetadata('tasks').replace(/[$@%"]/g, '') : ' ';
+ const searchParams = `tasks=${currentTasks}&phformat=${getMetadata('placeholder-format')}&topics=${topicsQuery}&ckgid=${d.ckgID}`;
+ const clone = linkPill.cloneNode(true);
+
+ clone.innerHTML = clone.innerHTML.replace('/express/templates/default', `${urlPrefix}/express/templates/search?${searchParams}`);
+ clone.innerHTML = clone.innerHTML.replaceAll('Default', displayText);
+ searchLinks.push(clone);
+ }
+ });
+
+ pageLinks.concat(searchLinks).forEach((clone) => {
+ container.append(clone);
+ });
+
+ if (container.children.length === 0) {
+ const linkListData = [];
+
+ window.linkLists.sheetData.forEach((row) => {
+ if (row.parent === getMetadata('short-title')) {
+ linkListData.push({
+ childSibling: row['child-siblings'],
+ shortTitle: getMetadata('short-title'),
+ tasks: getMetadata('tasks'),
+ });
+ }
+ });
+
+ linkListData.forEach((d) => {
+ const templatePageData = templatePages.find((p) => p.live === 'Y' && p.shortTitle === d.childSibling);
+ const clone = replaceLinkPill(linkPill, templatePageData);
+ container.append(clone);
+ });
+ }
+ }
+}
+
+async function lazyLoadLinklist() {
+ await fetchLinkList();
+ const linkList = document.querySelector('.link-list.fullwidth');
+
+ if (linkList) {
+ const linkListContainer = linkList.querySelector('p').parentElement;
+ const linkListTemplate = linkList.querySelector('p').cloneNode(true);
+ const linkListData = [];
+
+ if (window.linkLists && window.linkLists.ckgData && getMetadata('short-title')) {
+ window.linkLists.ckgData.forEach((row) => {
+ linkListData.push({
+ childSibling: row['child-siblings'],
+ ckgID: row.ckgID,
+ shortTitle: getMetadata('short-title'),
+ tasks: row.parent,
+ displayValue: row.displayValue,
+ });
+ });
+ }
+
+ await updateLinkList(linkListContainer, linkListTemplate, linkListData);
+ linkList.style.visibility = 'visible';
+ } else {
+ linkList?.remove();
+ }
+}
+
+async function lazyLoadSEOLinkList() {
+ await fetchLinkList();
+ const seoNav = document.querySelector('.seo-nav');
+
+ if (seoNav) {
+ const topTemplatesContainer = seoNav.querySelector('p').parentElement;
+ const topTemplates = getMetadata('top-templates');
+ if (topTemplates) {
+ const topTemplatesTemplate = seoNav.querySelector('p').cloneNode(true);
+ const topTemplatesData = topTemplates.split(', ').map((cs) => ({ childSibling: cs }));
+
+ await updateSEOLinkList(topTemplatesContainer, topTemplatesTemplate, topTemplatesData);
+ topTemplatesContainer.style.visibility = 'visible';
+ } else {
+ topTemplatesContainer.innerHTML = '';
+ }
+ }
+}
+
+async function lazyLoadSearchMarqueeLinklist() {
+ await fetchLinkList();
+ const searchMarquee = document.querySelector('.search-marquee');
+
+ if (searchMarquee) {
+ const linkListContainer = searchMarquee.querySelector('.carousel-container > .carousel-platform');
+ if (linkListContainer) {
+ const linkListTemplate = linkListContainer.querySelector('p').cloneNode(true);
+
+ const linkListData = [];
+
+ if (window.linkLists && window.linkLists.ckgData && getMetadata('short-title')) {
+ window.linkLists.ckgData.forEach((row) => {
+ linkListData.push({
+ childSibling: row['child-siblings'],
+ ckgID: row.ckgID,
+ shortTitle: getMetadata('short-title'),
+ tasks: row.parent, // task on the page
+ displayValue: row.displayValue,
+ });
+ });
+ }
+
+ await updateLinkList(linkListContainer, linkListTemplate, linkListData);
+ linkListContainer.parentElement.classList.add('appear');
+ }
+ }
+}
+
+function hideAsyncBlocks() {
+ const linkList = document.querySelector('.link-list.fullwidth');
+ const seoNav = document.querySelector('.seo-nav');
+
+ if (linkList) {
+ linkList.style.visibility = 'hidden';
+ }
+
+ if (seoNav) {
+ const topTemplatesContainer = seoNav.querySelector('p').parentElement;
+ topTemplatesContainer.style.visibility = 'hidden';
+ }
+}
+
+(async function updateAsyncBlocks() {
+ hideAsyncBlocks();
+ // TODO: integrate memoization
+ const showSearchMarqueeLinkList = getMetadata('show-search-marquee-link-list');
+ if (document.body.dataset.device === 'desktop' && (!showSearchMarqueeLinkList || ['yes', 'true', 'on', 'Y'].includes(showSearchMarqueeLinkList))) {
+ await lazyLoadSearchMarqueeLinklist();
+ }
+ await lazyLoadLinklist();
+ await lazyLoadSEOLinkList();
+}());
diff --git a/express/scripts/content-replace.js b/express/scripts/content-replace.js
new file mode 100644
index 000000000..8eb5210dc
--- /dev/null
+++ b/express/scripts/content-replace.js
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import {
+ fetchPlaceholders,
+ getMetadata,
+ titleCase,
+ createTag,
+} from './scripts.js';
+import fetchAllTemplatesMetadata from './all-templates-metadata.js';
+
+async function replaceDefaultPlaceholders(template) {
+ template.innerHTML = template.innerHTML.replaceAll('https://www.adobe.com/express/templates/default-create-link', getMetadata('create-link') || '/');
+
+ if (getMetadata('tasks') === '') {
+ const placeholders = await fetchPlaceholders();
+ template.innerHTML = template.innerHTML.replaceAll('default-create-link-text', placeholders['start-from-scratch'] || '');
+ } else {
+ template.innerHTML = template.innerHTML.replaceAll('default-create-link-text', getMetadata('create-text') || '');
+ }
+}
+
+async function getReplacementsFromSearch() {
+ const params = new Proxy(new URLSearchParams(window.location.search), {
+ get: (searchParams, prop) => searchParams.get(prop),
+ });
+ const {
+ tasks,
+ phformat,
+ topics,
+ q,
+ } = params;
+ if (!tasks && !phformat) {
+ return null;
+ }
+ const placeholders = await fetchPlaceholders();
+ const categories = JSON.parse(placeholders['task-categories']);
+ if (!categories) {
+ return null;
+ }
+ const tasksPair = Object.entries(categories).find((cat) => cat[1] === tasks);
+ const sanitizedTasks = tasks === "''" ? '' : tasks;
+ const sanitizedTopics = topics === "''" ? '' : topics;
+ const sanitizedQuery = q === "''" ? '' : q;
+ const translatedTasks = tasksPair ? tasksPair[0].toLowerCase() : tasks;
+ return {
+ '{{queryTasks}}': sanitizedTasks || '',
+ '{{QueryTasks}}': titleCase(sanitizedTasks || ''),
+ '{{translatedTasks}}': translatedTasks || '',
+ '{{TranslatedTasks}}': titleCase(translatedTasks || ''),
+ '{{placeholderRatio}}': phformat || '',
+ '{{QueryTopics}}': titleCase(sanitizedTopics || ''),
+ '{{queryTopics}}': sanitizedTopics || '',
+ '{{query}}': sanitizedQuery || '',
+ };
+}
+
+const bladeRegex = /\{\{[a-zA-Z_-]+\}\}/g;
+function replaceBladesInStr(str, replacements) {
+ if (!replacements) return str;
+ return str.replaceAll(bladeRegex, (match) => {
+ if (match in replacements) {
+ return replacements[match];
+ }
+ return match;
+ });
+}
+
+// for backwards compatibility
+// TODO: remove this func after all content is updated
+// legacy json -> metadata & dom blades
+await (async function updateLegacyContent() {
+ const searchMarquee = document.querySelector('.search-marquee');
+ if (searchMarquee) {
+ // not legacy
+ return;
+ }
+ const legacyAllTemplatesMetadata = await fetchAllTemplatesMetadata();
+ const data = legacyAllTemplatesMetadata.find((p) => p.url === window.location.pathname);
+ if (!data) return;
+ if (['yes', 'true', 'on', 'Y'].includes(getMetadata('template-search-page'))) {
+ const replacements = await getReplacementsFromSearch();
+ if (!replacements) return;
+ for (const key of Object.keys(data)) {
+ data[key] = replaceBladesInStr(data[key], replacements);
+ }
+ }
+
+ const heroAnimation = document.querySelector('.hero-animation.wide');
+ const templateList = document.querySelector('.template-list.fullwidth.apipowered');
+
+ const head = document.querySelector('head');
+ Object.keys(data).forEach((metadataKey) => {
+ const existingMetadataTag = head.querySelector(`meta[name=${metadataKey}]`);
+ if (existingMetadataTag) {
+ existingMetadataTag.setAttribute('content', data[metadataKey]);
+ } else {
+ head.append(createTag('meta', { name: `${metadataKey}`, content: data[metadataKey] }));
+ }
+ });
+
+ if (heroAnimation) {
+ if (data.heroAnimationTitle) {
+ heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template title', data.heroAnimationTitle);
+ }
+
+ if (data.heroAnimationText) {
+ heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template text', data.heroAnimationText);
+ }
+ }
+
+ if (templateList) {
+ const regex = /default-[a-zA-Z_-]+/g;
+ const replacements = {
+ 'default-title': data.shortTitle || '',
+ 'default-tasks': data.templateTasks || '',
+ 'default-topics': data.templateTopics || '',
+ 'default-locale': data.templateLocale || 'en',
+ 'default-premium': data.templatePremium || '',
+ 'default-animated': data.templateAnimated || '',
+ 'default-format': data.placeholderFormat || '',
+ };
+ templateList.innerHTML = templateList.innerHTML.replaceAll(regex, (match) => {
+ if (match in replacements) {
+ return replacements[match];
+ }
+ return match;
+ }).replaceAll('https://www.adobe.com/express/templates/default-create-link', data.createLink || '/');
+
+ if (data.templateTasks === '') {
+ const placeholders = await fetchPlaceholders();
+ templateList.innerHTML = templateList.innerHTML.replaceAll('default-create-link-text', placeholders['start-from-scratch'] || '');
+ } else {
+ templateList.innerHTML = templateList.innerHTML.replaceAll('default-create-link-text', data.createText || '');
+ }
+ }
+}());
+
+// searchbar -> metadata blades
+await (async function updateMetadataForTemplates() {
+ if (!['yes', 'true', 'on', 'Y'].includes(getMetadata('template-search-page'))) {
+ return;
+ }
+ const head = document.querySelector('head');
+ if (head) {
+ const replacements = await getReplacementsFromSearch();
+ if (!replacements) return;
+ head.innerHTML = replaceBladesInStr(head.innerHTML, replacements);
+ }
+}());
+
+// metadata -> dom blades
+(function autoUpdatePage() {
+ const wl = ['{{heading_placeholder}}', '{{type}}', '{{quantity}}'];
+ // FIXME: deprecate wl
+ const main = document.querySelector('main');
+ if (!main) return;
+ const regex = /\{\{([a-zA-Z_-]+)\}\}/g;
+ main.innerHTML = main.innerHTML.replaceAll(regex, (match, p1) => {
+ if (!wl.includes(match.toLowerCase())) {
+ return getMetadata(p1);
+ }
+ return match;
+ });
+}());
+
+// cleanup remaining dom blades
+(async function updateNonBladeContent() {
+ const heroAnimation = document.querySelector('.hero-animation.wide');
+ const templateList = document.querySelector('.template-list.fullwidth.apipowered');
+ const templateX = document.querySelector('.template-x');
+ const browseByCat = document.querySelector('.browse-by-category');
+ const seoNav = document.querySelector('.seo-nav');
+
+ if (heroAnimation) {
+ if (getMetadata('hero-title')) {
+ heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template title', getMetadata('hero-title'));
+ }
+
+ if (getMetadata('hero-text')) {
+ heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template text', getMetadata('hero-text'));
+ }
+ }
+
+ if (templateList) {
+ await replaceDefaultPlaceholders(templateList);
+ }
+
+ if (templateX) {
+ await replaceDefaultPlaceholders(templateX);
+ }
+
+ if (seoNav) {
+ if (getMetadata('top-templates-title')) {
+ seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates title', getMetadata('top-templates-title'));
+ }
+
+ if (getMetadata('top-templates-text')) {
+ seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates text', getMetadata('top-templates-text'));
+ } else {
+ seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates text', '');
+ }
+ }
+
+ if (browseByCat && !['yes', 'true', 'on', 'Y'].includes(getMetadata('show-browse-by-category'))) {
+ browseByCat.remove();
+ }
+}());
diff --git a/express/scripts/gnav.js b/express/scripts/gnav.js
index 5dc9a26cd..a84059a12 100644
--- a/express/scripts/gnav.js
+++ b/express/scripts/gnav.js
@@ -152,7 +152,7 @@ async function loadFEDS() {
async function buildBreadCrumbArray() {
if (isHomepage || getMetadata('hide-breadcrumbs') === 'true') {
- return undefined;
+ return null;
}
const capitalize = (word) => word.charAt(0).toUpperCase() + word.slice(1);
const buildBreadCrumb = (path, name, parentPath = '') => (
@@ -160,31 +160,27 @@ async function loadFEDS() {
);
const placeholders = await fetchPlaceholders();
- const validCategories = ['create', 'feature', 'templates'];
- const pathSegments = window.location.pathname.split('/')
- .filter((element) => element !== '')
- .filter((element) => element !== locale);
+ const validSecondPathSegments = ['create', 'feature'];
+ const pathSegments = window.location.pathname
+ .split('/')
+ .filter((e) => e !== locale);
const localePath = locale === 'us' ? '' : `${locale}/`;
- let category = pathSegments[1];
- const secondPathSegment = category.toLowerCase();
- const pagesShortNameElement = document.querySelector('meta[name="short-title"]');
- const pagesShortName = pagesShortNameElement ? pagesShortNameElement.getAttribute('content') : null;
+ const secondPathSegment = pathSegments[1].toLowerCase();
+ const pagesShortNameElement = document.head.querySelector('meta[name="short-title"]');
+ const pagesShortName = pagesShortNameElement?.getAttribute('content') ?? null;
+ const replacedCategory = placeholders[`breadcrumbs-${secondPathSegment}`];
- if ((!pagesShortName && pathSegments.length > 2)
- || !placeholders[`breadcrumbs-${category}`]
+ if (!pagesShortName
+ || pathSegments.length <= 2
+ || !replacedCategory
+ || !validSecondPathSegments.includes(replacedCategory)
|| locale !== 'us') { // Remove this line once locale translations are complete
- return undefined;
+ return null;
}
- category = capitalize(placeholders[`breadcrumbs-${category}`]);
- validCategories.push(category);
- const secondBreadCrumb = buildBreadCrumb(secondPathSegment, category, `${localePath}/express`);
+ const secondBreadCrumb = buildBreadCrumb(secondPathSegment, capitalize(replacedCategory), `${localePath}/express`);
const breadCrumbList = [secondBreadCrumb];
- if (!validCategories.includes(category)) {
- return undefined;
- }
-
if (pathSegments.length >= 3) {
const thirdBreadCrumb = buildBreadCrumb(pagesShortName, pagesShortName, secondBreadCrumb.url);
breadCrumbList.push(thirdBreadCrumb);
diff --git a/express/scripts/scripts.js b/express/scripts/scripts.js
index 82a613022..9a60e6174 100644
--- a/express/scripts/scripts.js
+++ b/express/scripts/scripts.js
@@ -1773,13 +1773,13 @@ export async function fetchFloatingCta(path) {
}
if (['yes', 'true', 'on'].includes(dev) && env && env.name === 'stage') {
- spreadsheet = '/express/floating-cta-dev.json?limit=10000';
+ spreadsheet = '/express/floating-cta-dev.json?limit=100000';
} else {
- spreadsheet = '/express/floating-cta.json?limit=10000';
+ spreadsheet = '/express/floating-cta.json?limit=100000';
}
if (experimentStatus === 'active') {
- const expSheet = '/express/experiments/floating-cta-experiments.json?limit=10000';
+ const expSheet = '/express/experiments/floating-cta-experiments.json?limit=100000';
floatingBtnData = await fetchFloatingBtnData(expSheet);
}
@@ -2328,8 +2328,14 @@ async function loadEager() {
}
if (!window.hlx.lighthouse) await decorateTesting();
- if (window.location.href.includes('/express/templates/')) {
- await import('./templates.js');
+ // for backward compatibility
+ // TODO: remove the href check after we tag content with sheet-powered
+ if (getMetadata('sheet-powered') === 'Y' || window.location.href.includes('/express/templates/')) {
+ await import('./content-replace.js');
+ }
+
+ if (getMetadata('template-search-page') === 'Y') {
+ await import('./template-redirect.js');
}
if (main) {
@@ -2342,10 +2348,14 @@ async function loadEager() {
displayOldLinkWarning();
wordBreakJapanese();
- const lcpBlocks = ['columns', 'hero-animation', 'hero-3d', 'template-list', 'floating-button', 'fullscreen-marquee', 'collapsible-card'];
- const block = document.querySelector('.block');
- const hasLCPBlock = (block && lcpBlocks.includes(block.getAttribute('data-block-name')));
- if (hasLCPBlock) await loadBlock(block, true);
+ const lcpBlocks = ['columns', 'hero-animation', 'hero-3d', 'template-list', 'floating-button', 'fullscreen-marquee', 'collapsible-card', 'search-marquee'];
+ const blocks = document.querySelectorAll('.block');
+ const firstVisualBlock = Array.from(blocks).find((b) => {
+ const { audience } = b.closest('.section')?.dataset || {};
+ return audience === document.body.dataset.device;
+ });
+ const hasLCPBlock = (firstVisualBlock && lcpBlocks.includes(firstVisualBlock.getAttribute('data-block-name')));
+ if (hasLCPBlock) await loadBlock(firstVisualBlock, true);
document.querySelector('body').classList.add('appear');
@@ -2367,7 +2377,9 @@ async function loadEager() {
await new Promise((resolve) => {
if (lcpCandidate && !lcpCandidate.complete) {
lcpCandidate.setAttribute('loading', 'eager');
- lcpCandidate.addEventListener('load', () => resolve());
+ lcpCandidate.addEventListener('load', () => {
+ resolve();
+ });
lcpCandidate.addEventListener('error', () => resolve());
} else {
resolve();
diff --git a/express/scripts/template-redirect.js b/express/scripts/template-redirect.js
new file mode 100644
index 000000000..1e07e1f3f
--- /dev/null
+++ b/express/scripts/template-redirect.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 Adobe. All rights reserved.
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import { getHelixEnv, getLocale, getMetadata } from './scripts.js';
+import fetchAllTemplatesMetadata from './all-templates-metadata.js';
+
+async function existsTemplatePage(url) {
+ const allTemplatesMetadata = await fetchAllTemplatesMetadata();
+ return allTemplatesMetadata.some((e) => e.url === url);
+}
+
+(function validatePage() {
+ const env = getHelixEnv();
+ const title = document.querySelector('title');
+ if ((env && env.name !== 'stage') && getMetadata('live') === 'N') {
+ window.location.replace('/express/templates/');
+ }
+
+ if ((env && env.name !== 'stage') || (title && title.innerText.match(/{{(.*?)}}/))) {
+ window.location.replace('/404');
+ }
+}());
+
+(async function redirectToExistingPage() {
+ // TODO: check if the search query points to an existing page. If so, redirect.
+ const params = new Proxy(new URLSearchParams(window.location.search), {
+ get: (searchParams, prop) => searchParams.get(prop),
+ });
+ if (params.topics) {
+ const targetPath = `/express/templates/${params.tasks}`.concat(params.topics ? `/${params.topics}` : '');
+ const locale = getLocale(window.location);
+ const pathToMatch = locale === 'us' ? targetPath : `/${locale}${targetPath}`;
+ if (await existsTemplatePage(pathToMatch)) {
+ window.location.replace(`${window.location.origin}${pathToMatch}`);
+ }
+ }
+}());
diff --git a/express/scripts/templates.js b/express/scripts/templates.js
deleted file mode 100644
index 94ce6f272..000000000
--- a/express/scripts/templates.js
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright 2022 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-import {
- getHelixEnv,
- arrayToObject,
- titleCase,
- createTag,
- fetchPlaceholders,
- getLocale,
- getMetadata,
-} from './scripts.js';
-
-import {
- fetchLinkListFromCKGApi,
- getPillWordsMapping,
-} from './api-v3-controller.js';
-
-export function findMatchExistingSEOPage(path) {
- const pathMatch = (e) => e.path === path;
- return (window.templates && window.templates.data.some(pathMatch));
-}
-
-export async function fetchPageContent(path) {
- const env = getHelixEnv();
- const dev = new URLSearchParams(window.location.search).get('dev');
- let sheet;
-
- if (['yes', 'true', 'on'].includes(dev) && env && env.name === 'stage') {
- sheet = '/templates-dev.json?sheet=seo-templates&limit=10000';
- } else {
- sheet = '/express/templates/content.json?sheet=seo-templates&limit=10000';
- }
-
- if (!(window.templates && window.templates.data)) {
- window.templates = {};
- const resp = await fetch(sheet);
- window.templates.data = resp.ok ? (await resp.json()).data : [];
- }
-
- const page = window.templates.data.find((p) => p.path === path);
-
- if (env && env.name === 'stage') {
- return page || null;
- }
-
- return page && page.live !== 'N' ? page : null;
-}
-
-async function formatSearchQuery(data) {
- // todo check if the search query points to an existing page. If so, redirect.
- const params = new Proxy(new URLSearchParams(window.location.search), {
- get: (searchParams, prop) => searchParams.get(prop),
- });
-
- const locale = getLocale(window.location);
- const targetPath = `/express/templates/${params.tasks}`.concat(params.topics ? `/${params.topics}` : '');
- const pathToMatch = locale === 'us' ? targetPath : `/${locale}${targetPath}`;
-
- if (findMatchExistingSEOPage(pathToMatch)) {
- window.location.replace(`${window.location.origin}${pathToMatch}`);
- }
-
- const dataArray = Object.entries(data);
-
- if (params.tasks && params.phformat) {
- const placeholders = await fetchPlaceholders();
- const categories = JSON.parse(placeholders['task-categories']);
- if (categories) {
- const TasksPair = Object.entries(categories).find((cat) => cat[1] === params.tasks);
- const translatedTasks = TasksPair ? TasksPair[0].toLowerCase() : params.tasks;
- dataArray.forEach((col) => {
- col[1] = col[1].replace('{{queryTasks}}', params.tasks || '');
- col[1] = col[1].replace('{{QueryTasks}}', titleCase(params.tasks || ''));
- col[1] = col[1].replace('{{translatedTasks}}', translatedTasks || '');
- col[1] = col[1].replace('{{TranslatedTasks}}', titleCase(translatedTasks || ''));
- col[1] = col[1].replace('{{placeholderRatio}}', params.phformat || '');
- col[1] = col[1].replace('{{QueryTopics}}', titleCase(params.topics || ''));
- col[1] = col[1].replace('{{queryTopics}}', params.topics || '');
- });
- }
- } else {
- return false;
- }
-
- return arrayToObject(dataArray);
-}
-
-async function fetchLinkList(data) {
- if (!window.linkLists) {
- window.linkLists = {};
- if (!window.linkLists.ckgData) {
- const response = await fetchLinkListFromCKGApi(data);
- // catch data from CKG API, if empty, use top priority categories sheet
- if (response && response.queryResults[0].facets) {
- window.linkLists.ckgData = response.queryResults[0].facets[0].buckets.map((ckgItem) => {
- let formattedTasks;
- if (getMetadata('template-search-page') === 'Y') {
- const params = new Proxy(new URLSearchParams(window.location.search), {
- get: (searchParams, prop) => searchParams.get(prop),
- });
- formattedTasks = titleCase(params.tasks).replace(/[$@%"]/g, '');
- } else {
- formattedTasks = titleCase(data.templateTasks).replace(/[$@%"]/g, '');
- }
-
- return {
- parent: formattedTasks,
- 'child-siblings': `${titleCase(ckgItem.displayValue)} ${formattedTasks}`,
- ckgID: ckgItem.canonicalName,
- displayValue: ckgItem.displayValue,
- };
- });
- }
- }
-
- if (!window.linkLists.sheetData) {
- const resp = await fetch('/express/templates/top-priority-categories.json');
- window.linkLists.sheetData = resp.ok ? (await resp.json()).data : [];
- }
- }
-}
-
-function matchCKGResult(ckgData, pageData) {
- const ckgMatch = pageData.ckgID === ckgData.ckgID;
- const taskMatch = ckgData.tasks.toLowerCase() === pageData.templateTasks.toLowerCase();
- const currentLocale = getLocale(window.location);
- const pageLocale = pageData.path.split('/')[1] === 'express' ? 'us' : pageData.path.split('/')[1];
- const sameLocale = currentLocale === pageLocale;
-
- return sameLocale && ckgMatch && taskMatch;
-}
-
-function replaceLinkPill(linkPill, data) {
- const clone = linkPill.cloneNode(true);
- if (data) {
- clone.innerHTML = clone.innerHTML.replace('/express/templates/default', data.path);
- clone.innerHTML = clone.innerHTML.replaceAll('Default', data.altShortTitle || data.shortTitle);
- }
- return clone;
-}
-
-function updateSEOLinkList(container, linkPill, list) {
- const templatePages = window.templates.data ?? [];
- container.innerHTML = '';
-
- if (list && templatePages) {
- list.forEach((d) => {
- const currentLocale = getLocale(window.location);
- const templatePageData = templatePages.find((p) => {
- const targetLocale = /^[a-z]{2}$/.test(p.path.split('/')[1]) ? p.path.split('/')[1] : 'us';
- const isLive = p.live === 'Y';
- const titleMatch = p.shortTitle.toLowerCase() === d.childSibling.toLowerCase();
- const localeMatch = currentLocale === targetLocale;
-
- return isLive && titleMatch && localeMatch;
- });
- const clone = replaceLinkPill(linkPill, templatePageData);
- container.append(clone);
- });
- }
-}
-
-function formatLinkPillText(pageData, linkPillData) {
- const digestedDisplayValue = titleCase(linkPillData.displayValue.replace(/-/g, ' '));
- const digestedChildSibling = titleCase(linkPillData.childSibling.replace(/-/g, ' '));
- const topics = pageData.templateTopics !== '" "' ? `${pageData.templateTopics.replace(/[$@%"]/g, '').replace(/-/g, ' ')}` : '';
-
- const displayTopics = topics && linkPillData.childSibling.indexOf(titleCase(topics)) < 0 ? titleCase(topics) : '';
- let displayText;
-
- if (pageData.templateTasks) {
- displayText = `${displayTopics} ${digestedDisplayValue} ${digestedChildSibling}`
- .split(' ')
- .filter((item, i, allItems) => i === allItems.indexOf(item))
- .join(' ').trim();
- } else {
- displayText = `${digestedDisplayValue} ${digestedChildSibling} ${displayTopics}`
- .split(' ')
- .filter((item, i, allItems) => i === allItems.indexOf(item))
- .join(' ').trim();
- }
-
- return displayText;
-}
-
-async function updateLinkList(container, linkPill, list, pageData) {
- const templatePages = window.templates.data ?? [];
- const pillsMapping = await getPillWordsMapping();
- const pageLinks = [];
- const searchLinks = [];
- container.innerHTML = '';
-
- if (list && templatePages) {
- list.forEach((d) => {
- const topics = pageData.templateTopics !== '" "' ? `${pageData.templateTopics.replace(/[$@%"]/g, '')}` : '';
- const templatePageData = templatePages.find((p) => p.live === 'Y' && matchCKGResult(d, p));
- const topicsQuery = `${topics ?? topics} ${d.displayValue}`.split(' ')
- .filter((item, i, allItems) => i === allItems.indexOf(item))
- .join(' ').trim();
- let displayText = formatLinkPillText(pageData, d);
-
- const locale = getLocale(window.location);
- const urlPrefix = locale === 'us' ? '' : `/${locale}`;
- const localeColumnString = locale === 'us' ? 'EN' : locale.toUpperCase();
- let hideUntranslatedPill = false;
-
- if (pillsMapping) {
- const alternateText = pillsMapping.find((row) => pageData.path === `${urlPrefix}${row['Express SEO URL']}` && d.ckgID === row['CKG Pill ID']);
-
- if (alternateText && alternateText[`${localeColumnString}`]) {
- displayText = alternateText[`${localeColumnString}`];
- if (templatePageData) {
- templatePageData.altShortTitle = displayText;
- }
- }
-
- hideUntranslatedPill = displayText && locale !== 'us';
- }
-
- if (templatePageData) {
- const clone = replaceLinkPill(linkPill, templatePageData);
- pageLinks.push(clone);
- } else if (d.ckgID && !hideUntranslatedPill) {
- const currentTasks = pageData.templateTasks ? pageData.templateTasks.replace(/[$@%"]/g, '') : ' ';
-
- const searchParams = `tasks=${currentTasks}&phformat=${pageData.placeholderFormat}&topics=${topicsQuery}&ckgid=${d.ckgID}`;
- const clone = linkPill.cloneNode(true);
-
- clone.innerHTML = clone.innerHTML.replace('/express/templates/default', `${urlPrefix}/express/templates/search?${searchParams}`);
- clone.innerHTML = clone.innerHTML.replaceAll('Default', displayText);
- searchLinks.push(clone);
- }
-
- pageLinks.concat(searchLinks).forEach((clone) => {
- container.append(clone);
- });
- });
-
- if (container.children.length === 0) {
- const linkListData = [];
-
- window.linkLists.sheetData.forEach((row) => {
- if (row.parent === pageData.shortTitle) {
- linkListData.push({
- childSibling: row['child-siblings'],
- shortTitle: pageData.shortTitle,
- tasks: pageData.templateTasks,
- });
- }
- });
-
- linkListData.forEach((d) => {
- const templatePageData = templatePages.find((p) => p.live === 'Y' && p.shortTitle === d.childSibling);
- replaceLinkPill(linkPill, templatePageData, container);
- });
- }
- }
-}
-
-function updateMetadata(data) {
- const $head = document.querySelector('head');
- const $title = $head.querySelector('title');
- let $metaTitle = document.querySelector('meta[property="og:title"]');
- let $twitterTitle = document.querySelector('meta[name="twitter:title"]');
- let $description = document.querySelector('meta[property="og:description"]');
-
- if ($title) {
- $title.textContent = data.metadataTitle;
-
- if ($metaTitle) {
- $metaTitle.setAttribute('content', data.metadataTitle);
- } else {
- $metaTitle = createTag('meta', { property: 'og:title', content: data.metadataTitle });
- $head.append($metaTitle);
- }
-
- if ($description) {
- $description.setAttribute('content', data.metadataDescription);
- } else {
- $description = createTag('meta', { property: 'og:description', content: data.metadataDescription });
- $head.append($description);
- }
-
- if ($twitterTitle) {
- $twitterTitle.setAttribute('content', data.metadataTitle);
- } else {
- $twitterTitle = createTag('meta', { property: 'twitter:title', content: data.metadataTitle });
- $head.append($twitterTitle);
- }
- }
-}
-
-function formatAllTaskText(data) {
- const formattedData = data;
-
- if (formattedData.templateTasks === "''" || formattedData.templateTopics === "''") {
- Object.entries(formattedData).forEach((entry) => {
- formattedData[entry[0]] = entry[1].replace("''", '');
- });
- }
-
- return formattedData;
-}
-
-async function updateBlocks(data) {
- const heroAnimation = document.querySelector('.hero-animation.wide');
- const linkList = document.querySelector('.link-list.fullwidth');
- const templateList = document.querySelector('.template-list.fullwidth.apipowered');
- const seoNav = document.querySelector('.seo-nav');
-
- if (data.shortTitle) {
- const shortTitle = createTag('meta', { name: 'short-title', content: data.shortTitle });
- const $head = document.querySelector('head');
- $head.append(shortTitle);
- }
-
- if (heroAnimation) {
- if (data.heroAnimationTitle) {
- heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template title', data.heroAnimationTitle);
- }
-
- if (data.heroAnimationText) {
- heroAnimation.innerHTML = heroAnimation.innerHTML.replace('Default template text', data.heroAnimationText);
- }
- }
-
- const linkListContainer = linkList.querySelector('p').parentElement;
-
- if (linkList && window.templates.data) {
- const linkListTemplate = linkList.querySelector('p').cloneNode(true);
- const linkListData = [];
-
- if (window.linkLists && window.linkLists.ckgData && data.shortTitle) {
- window.linkLists.ckgData.forEach((row) => {
- linkListData.push({
- childSibling: row['child-siblings'],
- ckgID: row.ckgID,
- shortTitle: data.shortTitle,
- tasks: row.parent,
- displayValue: row.displayValue,
- });
- });
- }
-
- await updateLinkList(linkListContainer, linkListTemplate, linkListData, data);
- } else {
- linkListContainer.remove();
- }
-
- if (templateList) {
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-title', data.shortTitle || '');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-tasks', data.templateTasks || '');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-topics', data.templateTopics || '');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-locale', data.templateLocale || 'en');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-premium', data.templatePremium || '');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-animated', data.templateAnimated || '');
- templateList.innerHTML = templateList.innerHTML.replaceAll('https://www.adobe.com/express/templates/default-create-link', data.createLink || '/');
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-format', data.placeholderFormat || '');
-
- if (data.templateTasks === '') {
- const placeholders = await fetchPlaceholders().then((result) => result);
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-create-link-text', placeholders['start-from-scratch'] || '');
- } else {
- templateList.innerHTML = templateList.innerHTML.replaceAll('default-create-link-text', data.createText || '');
- }
- }
-
- if (seoNav) {
- const topTemplatesContainer = seoNav.querySelector('p').parentElement;
-
- if (window.templates.data && data.topTemplates) {
- const topTemplatesTemplate = seoNav.querySelector('p').cloneNode(true);
- const topTemplatesData = data.topTemplates.split(', ').map((cs) => ({ childSibling: cs }));
-
- updateSEOLinkList(topTemplatesContainer, topTemplatesTemplate, topTemplatesData);
- } else {
- topTemplatesContainer.innerHTML = '';
- }
-
- if (data.topTemplatesTitle) {
- seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates title', data.topTemplatesTitle);
- }
-
- if (data.topTemplatesText) {
- seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates text', data.topTemplatesText);
- } else {
- seoNav.innerHTML = seoNav.innerHTML.replace('Default top templates text', '');
- }
- }
-}
-
-const page = await fetchPageContent(window.location.pathname);
-
-if (page) {
- await fetchLinkList(page);
- if (getMetadata('template-search-page') === 'Y') {
- const data = await formatSearchQuery(page);
- if (!data) {
- window.location.replace('/express/templates/');
- } else {
- const purgedData = formatAllTaskText(data);
- updateMetadata(purgedData);
- await updateBlocks(purgedData);
- }
- } else {
- await updateBlocks(page);
- }
-} else {
- const env = getHelixEnv();
-
- if ((env && env.name !== 'stage') || window.location.pathname !== '/express/templates/default') {
- window.location.replace('/404');
- }
-}
diff --git a/express/scripts/utils.js b/express/scripts/utils.js
index 69a3e2223..3fa7ca605 100644
--- a/express/scripts/utils.js
+++ b/express/scripts/utils.js
@@ -92,6 +92,7 @@ export function memoize(cb, { key = (...args) => args.join(','), ttl } = {}) {
}
return result;
} catch (e) {
+ // eslint-disable-next-line no-console
console.error('Memoized Callback Error: ', e);
throw e;
}