diff --git a/blocks/card-carousel/card-carousel.css b/blocks/card-carousel/card-carousel.css
index 27dbc18..14870de 100644
--- a/blocks/card-carousel/card-carousel.css
+++ b/blocks/card-carousel/card-carousel.css
@@ -2,6 +2,7 @@
margin: 0 auto;
max-width: calc(1032px + var(--padding--mobile) + var(--padding--mobile));
padding: var(--padding--mobile);
+ overflow: hidden;
}
.card-carousel .splide__track {
diff --git a/blocks/header/header.css b/blocks/header/header.css
index 71b8ec5..8628398 100644
--- a/blocks/header/header.css
+++ b/blocks/header/header.css
@@ -404,7 +404,7 @@
z-index: 100;
top: 0;
height: 0;
- width: 100%;
+ width: 100vw;
transition: opacity var(--action-ease-duration) var(--action-ease-function);
visibility: hidden;
display: flex;
diff --git a/blocks/header/header.js b/blocks/header/header.js
index c6e4057..17ba14a 100644
--- a/blocks/header/header.js
+++ b/blocks/header/header.js
@@ -8,11 +8,7 @@ import {
isMobile,
parseFragment,
render,
- renderBreadCrumbs,
} from '../../scripts/scripts.js';
-import '../language-selector/language-selector.js';
-import '../search-bar/search-bar.js';
-import '../theme-toggle/theme-toggle.js';
//
const TEMPLATE = /* html */ `
@@ -372,16 +368,19 @@ function addEventListeners(block) {
);
/* Search */
+ const emitLoadSearch = (() => {
+ let sent = false;
+ return () => {
+ if (sent) return false;
+ sent = true;
+ store.emit('load:search');
+ return true;
+ };
+ })();
const searchButtonOpen = desktopNav.querySelector('.nav-search-button');
const searchButtonClose = searchPanel.querySelector('.search-panel-close');
- // searchButtonOpen.addEventListener(
- // "mouseenter",
- // () => {
- // import("../search-bar/search-bar.js");
- // },
- // { once: true }
- // );
+ searchButtonOpen.addEventListener('mouseenter', emitLoadSearch, { once: true });
const focusSearchInput = () => {
// Check for searchbar
@@ -395,15 +394,23 @@ function addEventListeners(block) {
}
};
+ const openSearch = () => {
+ // delay to load search before opening first time
+ const delay = emitLoadSearch('load:search') ? 150 : 1;
+ setTimeout(() => {
+ searchPanel.classList.add('active');
+
+ // Focus on search input
+ focusSearchInput();
+ }, delay);
+ };
+
window.addEventListener('keydown', (e) => {
const { key } = e;
const searchIsActive = searchPanel.classList.contains('active');
if (key === '/' && !searchIsActive) {
- searchPanel.classList.add('active');
-
- // Focus on search input
- focusSearchInput();
+ openSearch();
}
if (key === 'Escape' && searchIsActive) {
@@ -412,10 +419,7 @@ function addEventListeners(block) {
});
searchButtonOpen.addEventListener('click', () => {
- searchPanel.classList.add('active');
-
- // Focus on search input
- focusSearchInput();
+ openSearch();
});
searchButtonClose.addEventListener('click', () => {
@@ -626,6 +630,11 @@ export default async function decorate(block) {
if (!isMobile()) {
// renderBreadCrumbs();
} else {
- store.once('delayed:loaded', renderBreadCrumbs);
+ // store.once('delayed:loaded', renderBreadCrumbs);
}
+
+ // load custom elements
+ import('../language-selector/language-selector.js');
+ import('../search-bar/search-bar.js');
+ import('../theme-toggle/theme-toggle.js');
}
diff --git a/blocks/hero-search/hero-search.js b/blocks/hero-search/hero-search.js
index b5a2b98..d7563ad 100644
--- a/blocks/hero-search/hero-search.js
+++ b/blocks/hero-search/hero-search.js
@@ -1,3 +1,4 @@
+import { loadCSS } from '../../scripts/lib-franklin.js';
import { html, renderParallax } from '../../scripts/scripts.js';
import '../search-bar/search-bar.js';
@@ -5,6 +6,7 @@ import '../search-bar/search-bar.js';
* @param {HTMLDivElement} block
*/
export default function decorate(block) {
+ loadCSS(`${window.hlx.codeBasePath}/blocks/search-bar/search-bar.css`);
block.append(html`
@@ -51,9 +53,7 @@ export default function decorate(block) {
-
-
- `;
+`;
const section = block.closest('div.section');
const wrapper = section.querySelector('.hero-search-wrapper');
diff --git a/blocks/search-bar/search-bar.css b/blocks/search-bar/search-bar.css
index 227319d..7421dbd 100644
--- a/blocks/search-bar/search-bar.css
+++ b/blocks/search-bar/search-bar.css
@@ -38,6 +38,56 @@ search-bar {
display: flex;
}
+.search-bar #coveo-searchbox #placeholder-searchbox {
+ flex-grow: 1;
+ display: flex;
+}
+
+.search-bar #coveo-searchbox #placeholder-searchbox div {
+ border-top: 1px solid var(--search-border-color);
+ border-bottom: 1px solid var(--search-border-color);
+ border-left: 1px solid var(--search-border-color);
+ border-radius: 0;
+ border-top-left-radius: var(--search-border-radius);
+ border-bottom-left-radius: var(--search-border-radius);
+ height: var(--search-height);
+ width: 43px;
+ min-width: 43px;
+ background: var(--search-background-color);
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+}
+
+.search-bar #coveo-searchbox #placeholder-searchbox input {
+ background: var(--search-background-color);
+ color: var(--search-text-color);
+ font-family: var(--body-font-family);
+ font-size: var(--control-font-size--sm);
+ line-height: var(--control-line-height--sm);
+ padding: 13px 16px 11px 1px;
+ padding-right: var(--spacing--4);
+ border: none;
+ resize: none;
+ outline: 0;
+ margin: 0;
+ border-top: 1px solid var(--search-border-color);
+ border-bottom: 1px solid var(--search-border-color);
+ border-radius: 0;
+ height: var(--search-height);
+ margin-left: -1px;
+ flex-grow: 1;
+}
+
+.search-bar #coveo-searchbox #placeholder-searchbox input::placeholder {
+ color: var(--search-placeholder);
+ padding-top: 10px;
+}
+
+.search-bar.coveo-ready #coveo-searchbox #placeholder-searchbox {
+ display: none;
+}
+
.search-bar #coveo-searchbox .magic-box {
border: none;
border-radius: 0;
@@ -247,22 +297,22 @@ search-bar {
}
.search-bar
- .magic-box.magic-box-hasFocus
- .magic-box-suggestions.magic-box-hasSuggestion
- .coveo-magicbox-suggestions,
+.magic-box.magic-box-hasFocus
+.magic-box-suggestions.magic-box-hasSuggestion
+.coveo-magicbox-suggestions,
.search-bar
- .magic-box.magic-box-hasFocus
- .magic-box-suggestions.magic-box-hasSuggestion
- .coveo-suggestion-container,
+.magic-box.magic-box-hasFocus
+.magic-box-suggestions.magic-box-hasSuggestion
+.coveo-suggestion-container,
.search-bar .magic-box .magic-box-suggestions .coveo-magicbox-suggestions {
border: none;
}
.search-bar .dropdown-content,
.search-bar
- .magic-box
- .magic-box-suggestions.magic-box-hasSuggestion
- .coveo-magicbox-suggestions {
+.magic-box
+.magic-box-suggestions.magic-box-hasSuggestion
+.coveo-magicbox-suggestions {
background: var(--search-background-color);
border-radius: var(--search-autosuggest-border-radius);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.08);
@@ -283,9 +333,9 @@ search-bar {
.search-bar .dropdown-content a:not(:last-child),
.search-bar
- .magic-box
- .magic-box-suggestions
- .magic-box-suggestion:not(:last-child) {
+.magic-box
+.magic-box-suggestions
+.magic-box-suggestion:not(:last-child) {
margin-bottom: var(--spacing--1);
}
diff --git a/blocks/search-bar/search-bar.js b/blocks/search-bar/search-bar.js
index 1a9a316..e6f6b32 100644
--- a/blocks/search-bar/search-bar.js
+++ b/blocks/search-bar/search-bar.js
@@ -22,15 +22,27 @@ const TEMPLATE = /* html */ `
`;
-const TEMPLATE_CLOSE_ICON = `
+const TEMPLATE_CLOSE_ICON = /* html */`
`;
@@ -58,6 +70,8 @@ export class SearchBar extends HTMLElement {
this.innerHTML = TEMPLATE;
this.root = this.firstElementChild;
this.coveoConfig = getCoveoConfig();
+ /** @type {Promise
} */
+ this.inputWhenReady = null;
// Allow for mutiple search bars to be initiated on one page
this.flags = {
@@ -65,7 +79,7 @@ export class SearchBar extends HTMLElement {
};
this.init();
- this.loadCoveo();
+ this.swapPlaceholderInput();
}
async loadCoveo() {
@@ -73,6 +87,29 @@ export class SearchBar extends HTMLElement {
this._initCoveo();
}
+ swapPlaceholderInput() {
+ let resolve;
+ this.inputWhenReady = new Promise((res) => {
+ resolve = res;
+ });
+
+ const searchbar = this.querySelector('.search-bar');
+ const searchbox = searchbar.querySelector('#coveo-searchbox');
+
+ const observer = new MutationObserver((records, self) => {
+ const ready = !!records.find(
+ (record) => !![...record.addedNodes]
+ .find((node) => node.classList.contains('CoveoOmnibox')),
+ );
+ if (ready) {
+ self.disconnect();
+ searchbar.classList.add('coveo-ready');
+ resolve(searchbox.querySelector('input:not(.placeholder)'));
+ }
+ });
+ observer.observe(searchbox, { childList: true });
+ }
+
init() {
const booknameMeta = getMetadata('book-name');
const productMeta = getMetadata('docset-title');
@@ -132,6 +169,29 @@ export class SearchBar extends HTMLElement {
this.querySelector('.dropbtn').textContent = targetOption.textContent;
}
}
+
+ // load coveo on-demand to avoid the bundle blocking initial page load.
+ // this happens either when the store gets a `load:search` event, eg. from header block
+ // or when the search bar is entered/touched, eg. on the homepage
+ store.once('load:search', () => {
+ this.loadCoveo();
+ });
+
+ this.addEventListener('mouseenter', () => {
+ this.loadCoveo();
+ }, { once: true });
+
+ this.addEventListener('touchstart', () => {
+ if (this.flags.hasInit) return;
+ this.loadCoveo();
+
+ // if the tap completes
+ this.addEventListener('touchend', async () => {
+ // resubmit the event after placeholder is swapped
+ const input = await this.inputWhenReady;
+ input.click();
+ }, { once: true, passive: true });
+ }, { once: true, passive: true });
}
static async LoadCoveo() {
@@ -152,84 +212,86 @@ export class SearchBar extends HTMLElement {
}
_initCoveo() {
+ if (Coveo.SearchEndpoint.defaultEndpoint !== undefined && this.flags.hasInit) {
+ return;
+ }
+
const { orgID, apiKey, searchPageURL } = this.coveoConfig;
- if (Coveo.SearchEndpoint.defaultEndpoint === undefined || !this.flags.hasInit) {
- const searchBoxRoot = this.querySelector('.searchbox');
- Coveo.SearchEndpoint.configureCloudV2Endpoint(orgID, apiKey);
- Coveo.$$(searchBoxRoot).on('newQuery', () => {
- const dropdownSelectedValue = this.querySelector(
- '.coveo-dropdown-item.selected',
- ).getAttribute('data-value');
- try {
- if (dropdownSelectedValue !== 'all') {
- Coveo.state(searchBoxRoot, 'hq', dropdownSelectedValue);
- Coveo.state(
- searchBoxRoot,
- 'hd',
- this.querySelector('.coveo-dropdown-item.selected').getAttribute('data-label').trim(),
- );
- } else {
- // eslint-disable-next-line no-undef
- Coveo.state(searchBoxRoot, 'hq', '');
- // eslint-disable-next-line no-undef
- Coveo.state(searchBoxRoot, 'hd', '');
- }
- } catch (error) {
- console.log(error);
- }
- });
- Coveo.initSearchbox(searchBoxRoot, searchPageURL);
- const dropDown = this.querySelector('.dropdown');
- const dropDownOpen = dropDown.querySelector('.dropbtn');
- const dropDownLoad = dropDown.querySelector('.dropdown-content');
-
- dropDownOpen.addEventListener('click', () => {
- dropDown.classList.toggle('is-active');
- if (dropDownLoad.style.display === 'none') {
- dropDownLoad.style.display = 'block';
+ const searchBoxRoot = this.querySelector('.searchbox');
+ Coveo.SearchEndpoint.configureCloudV2Endpoint(orgID, apiKey);
+ Coveo.$$(searchBoxRoot).on('newQuery', () => {
+ const dropdownSelectedValue = this.querySelector(
+ '.coveo-dropdown-item.selected',
+ ).getAttribute('data-value');
+ try {
+ if (dropdownSelectedValue !== 'all') {
+ Coveo.state(searchBoxRoot, 'hq', dropdownSelectedValue);
+ Coveo.state(
+ searchBoxRoot,
+ 'hd',
+ this.querySelector('.coveo-dropdown-item.selected').getAttribute('data-label').trim(),
+ );
} else {
- dropDownLoad.style.display = 'none';
+ // eslint-disable-next-line no-undef
+ Coveo.state(searchBoxRoot, 'hq', '');
+ // eslint-disable-next-line no-undef
+ Coveo.state(searchBoxRoot, 'hd', '');
}
- });
+ } catch (error) {
+ console.log(error);
+ }
+ });
+ Coveo.initSearchbox(searchBoxRoot, searchPageURL);
+ const dropDown = this.querySelector('.dropdown');
+ const dropDownOpen = dropDown.querySelector('.dropbtn');
+ const dropDownLoad = dropDown.querySelector('.dropdown-content');
+
+ dropDownOpen.addEventListener('click', () => {
+ dropDown.classList.toggle('is-active');
+ if (dropDownLoad.style.display === 'none') {
+ dropDownLoad.style.display = 'block';
+ } else {
+ dropDownLoad.style.display = 'none';
+ }
+ });
- // Set default input placeholder for selected item
- const defaultLabel = this.querySelector('.coveo-dropdown-item.selected').getAttribute(
- 'data-label',
- );
+ // Set default input placeholder for selected item
+ const defaultLabel = this.querySelector('.coveo-dropdown-item.selected').getAttribute(
+ 'data-label',
+ );
- const searchInput = this.querySelector('.magic-box-input input');
- searchInput.setAttribute('placeholder', `Search ${defaultLabel}`);
+ const searchInput = this.querySelector('.magic-box-input input');
+ searchInput.setAttribute('placeholder', `Search ${defaultLabel}`);
- for (const dropoption of this.querySelectorAll('.coveo-dropdown-item')) {
- dropoption.addEventListener('click', (event) => {
- const label = event.target.getAttribute('data-label');
- this.querySelector('.coveo-dropdown-item.selected').classList.remove('selected');
- event.target.classList.add('selected');
- this.querySelector('.dropbtn').textContent = label;
- dropDownLoad.setAttribute('style', 'display : none');
- dropDown.classList.remove('is-active');
+ for (const dropoption of this.querySelectorAll('.coveo-dropdown-item')) {
+ dropoption.addEventListener('click', (event) => {
+ const label = event.target.getAttribute('data-label');
+ this.querySelector('.coveo-dropdown-item.selected').classList.remove('selected');
+ event.target.classList.add('selected');
+ this.querySelector('.dropbtn').textContent = label;
+ dropDownLoad.setAttribute('style', 'display : none');
+ dropDown.classList.remove('is-active');
- searchInput.setAttribute('placeholder', `Search ${label}`);
- });
- }
+ searchInput.setAttribute('placeholder', `Search ${label}`);
+ });
+ }
- const updateClearButton = this.querySelector('.magic-box-clear .magic-box-icon');
+ const updateClearButton = this.querySelector('.magic-box-clear .magic-box-icon');
- if (updateClearButton) {
- updateClearButton.innerHTML = TEMPLATE_CLOSE_ICON;
- }
+ if (updateClearButton) {
+ updateClearButton.innerHTML = TEMPLATE_CLOSE_ICON;
+ }
- const searchBoxInput = this.querySelector('.magic-box-input input');
- searchBoxInput.addEventListener('focus', () => {
- searchBoxRoot.classList.add('is-focused');
- });
- searchBoxInput.addEventListener('blur', () => {
- searchBoxRoot.classList.remove('is-focused');
- });
+ const searchBoxInput = this.querySelector('.magic-box-input input');
+ searchBoxInput.addEventListener('focus', () => {
+ searchBoxRoot.classList.add('is-focused');
+ });
+ searchBoxInput.addEventListener('blur', () => {
+ searchBoxRoot.classList.remove('is-focused');
+ });
- this.flags.hasInit = true;
- }
+ this.flags.hasInit = true;
}
}
diff --git a/styles/styles.css b/styles/styles.css
index f9df784..757b853 100644
--- a/styles/styles.css
+++ b/styles/styles.css
@@ -387,6 +387,8 @@ body.landing-division {
),
#fff;
background-size: auto;
+ min-height: 100vh;
+ max-width: 100vw;
}
body.landing-division .section:not(.parallax-container) {
diff --git a/types/Events.d.ts b/types/Events.d.ts
index 86193ba..bb49955 100644
--- a/types/Events.d.ts
+++ b/types/Events.d.ts
@@ -14,6 +14,7 @@ import { ArticleInfo, ArticleResponse } from "./Article";
import { BookDefinition } from "./Book";
export interface EventMap {
+ "load:search": void;
"delayed:loaded": void;
"book:loaded": BookDefinition;
"article:fetched": ArticleResponse;