PageSourceSearch

https://veronicabeard.com/cdn/shop/t/1989/assets/predictive-search…?v=115204139741194717611790009629

js veronicabeard.com collected 2026-09-24 18:00:09 UTC 35,531 bytes, 89 lines download raw bytes

1import{Component}from"@theme/component";import{debounce,prefersReducedMotion}from"@theme/utilities";import{DialogCloseEvent,DialogOpenEvent}from"@theme/dialog";import{createHighlightRegex,productGridSkeletonHtml,productsInlineMarkup,productsPanelMarkup,suggestionsMarkup}from"@theme/predictive-search-constructor-render";import{buildAutocompleteOptions,formatSuggestionCount,getPredictiveSearchTranslations,normalizeAutocompleteResponse,toCacheKey}from"@theme/predictive-search-constructor-data";import{DEFAULT_BATCH_PREDICTIVE_SECTION_ID,escapeHtml,extractProductHandle,fetchBatchCards,getClonedCardFragment,hasBatchCards,waitForConstructorClient}from"@theme/constructor-utils";const INPUT_DEBOUNCE_MS=300,HOVER_DEBOUNCE_MS=150,HOVER_INTENT_PREFETCH_MS=70,MOBILE_PRODUCT_LIMIT=6,DESKTOP_PRODUCT_LIMIT=9,MOBILE_PRODUCT_FETCH_LIMIT=10,DESKTOP_PRODUCT_FETCH_LIMIT=15,POPULAR_SEARCH_TERMS_LIMIT=8,HOVER_PREFETCH_LIMIT=3;class PredictiveSearchConstructorComponent extends Component{requiredRefs=["searchInput","predictiveSearchResults","resetButton","productsPanel"];#controller=new AbortController;#searchAbortController=null;#hoverAbortController=null;#suggestionCache=new Map;#currentQuery="";#primarySuggestionQuery="";#hoveredSuggestion="";#hoverIntentSuggestion="";#isKeyboardNavigation=!1;#desktopProductsMedia=window.matchMedia("(min-width: 1080px)");#isDesktopProducts=this.#desktopProductsMedia.matches;#popularSearchTerms=[];#popularSearchResultId="";#popularSearchNumResults=0;#suggestionRequests=new Map;static#RECENT_STORAGE_KEY="cnstrc-predictive-search-recent";static#RECENT_MAX=5;get#searchInput(){return this.refs.searchInput}get#resultsContainer(){return this.refs.predictiveSearchResults}get#resetButtonEl(){return this.refs.resetButton}get#productsPanel(){return this.refs.productsPanel}get#dyRecommendations(){const raw=this.refs.dyRecommendations,element=Array.isArray(raw)?raw[0]:raw;return element instanceof HTMLElement?element:null}get dialog(){return this.closest("dialog-component")}connectedCallback(){super.connectedCallback();const{signal}=this.#controller,{dialog}=this;this.#searchInput.value.length>0&&this.#showResetButton(),this.#syncDyRecommendationsVisibility(),this.#searchInput.addEventListener("input",()=>{this.#syncDyRecommendationsVisibility()},{signal}),dialog&&(document.addEventListener("keydown",this.#handleKeyboardShortcut,{signal}),dialog.addEventListener(DialogCloseEvent.eventName,this.#handleDialogClose,{signal}),dialog.addEventListener(DialogOpenEvent.eventName,this.#handleDialogOpen,{signal}),this.addEventListener("click",this.#handleModalClick,{signal}),this.addEventListener("click",this.#handleSuggestionClick,{signal}),this.addEventListener("click",this.#handleResultsClick,{signal}),this.addEventListener("pointerenter",this.#handleSuggestionPointerEnter,{signal,capture:!0}),this.addEventListener("pointerleave",this.#handleComponentPointerLeave,{signal})),this.addEventListener("click",this.#handleRecentSearchesClick,{signal}),typeof this.#desktopProductsMedia.addEventListener=="function"?this.#desktopProductsMedia.addEventListener("change",this.#handleProductsMediaChange,{signal}):typeof this.#desktopProductsMedia.addListener=="function"&&(this.#desktopProductsMedia.addListener(this.#handleProductsMediaChange),signal.addEventListener("abort",()=>{this.#desktopProductsMedia.removeListener(this.#handleProductsMediaChange)},{once:!0})),this.#resetSearch(),(this.closest?.("dialog")??null)?.open&&!this.#searchInput.value.trim()&&requestAnimationFrame(()=>{this.#renderRecentSearches(),this.fetchPopularSearchTerms()})}disconnectedCallback(){super.disconnectedCallback(),this.#controller.abort(),this.#searchAbortController&&(this.#searchAbortController.abort(),this.#searchAbortController=null),this.#hoverAbortController&&(this.#hoverAbortController.abort(),this.#hoverAbortController=null),this.search.cancel(),this.#queueHoverSuggestion.cancel(),this.#queueHoverPrefetch.cancel(),this.#suggestionRequests.clear()}get#allResultsItems(){return Array.from(this.querySelectorAll(".cnstrc-predictive-search__suggestion-item, .cnstrc-predictive-search__products-grid .product-card, .cnstrc-predictive-search__products-grid .predictive-search-results__card")).filter(item=>item instanceof HTMLElement)}get#currentIndex(){return this.#allResultsItems?.findIndex(item=>item.getAttribute("aria-selected")==="true")??-1}
1set#currentIndex(index){if(this.#allResultsItems?.length){this.#allResultsItems.forEach(item=>{item.classList.remove("keyboard-focus")});for(const[itemIndex,item]of this.#allResultsItems.entries())itemIndex===index?(item.setAttribute("aria-selected","true"),this.#isKeyboardNavigation&&item.classList.add("keyboard-focus"),item.scrollIntoView({behavior:prefersReducedMotion()?"instant":"smooth",block:"nearest"})):item.removeAttribute("aria-selected")}}get#currentItem(){return this.#allResultsItems?.[this.#currentIndex]}onSearchKeyDown=event=>{if(event.key==="Escape"){this.#resetSearch();return}if(!this.#allResultsItems?.length||event.key==="ArrowLeft"||event.key==="ArrowRight")return;const currentIndex=this.#currentIndex,totalItems=this.#allResultsItems.length;switch(event.key){case"ArrowDown":this.#isKeyboardNavigation=!0,event.preventDefault(),this.#currentIndex=currentIndex<totalItems-1?currentIndex+1:0;break;case"ArrowUp":this.#isKeyboardNavigation=!0,event.preventDefault(),this.#currentIndex=currentIndex>0?currentIndex-1:totalItems-1;break;case"Enter":if(this.#currentIndex>=0)event.preventDefault(),this.#currentItem?.querySelector("a")?.click();else{const query=this.#searchInput.value.trim();if(this.#addToRecentSearches(query),!query)return;window.location.href=this.#buildSearchUrl(query)}break}};search=debounce(event=>{if(!event.inputType)return;const searchTerm=this.#searchInput.value.trim();if(this.#currentIndex=-1,!searchTerm.length){this.#resetSearch();return}this.#showResetButton(),this.#performSearch(searchTerm)},INPUT_DEBOUNCE_MS);#queueHoverSuggestion=debounce((query,resultCount)=>{this.#loadSuggestionProducts(query,resultCount)},HOVER_DEBOUNCE_MS);#queueHoverPrefetch=debounce((query,resultCount)=>{this.#prefetchSuggestionProducts(query,resultCount,{warmCards:!0})},HOVER_INTENT_PREFETCH_MS);#handleProductsMediaChange=event=>{const previousIsDesktop=this.#isDesktopProducts;if(this.#isDesktopProducts=event.matches,previousIsDesktop===this.#isDesktopProducts)return;const activeCacheKey=this.#activeProductsCacheKey(),cached=this.#suggestionCache.get(activeCacheKey);if(!cached){this.#setWithProductsPanel(!1),this.#removeInlineProducts();return}this.#renderProductsPanel({query:cached.query,products:cached.products,signal:null})};#getTranslations(){return getPredictiveSearchTranslations(typeof Theme<"u"?Theme?.translations:{})}#handleKeyboardShortcut=event=>{event.metaKey&&event.key==="k"&&this.dialog?.toggleDialog?.()};#handleDialogClose=()=>{this.#resetSearch()};#handleDialogOpen=()=>{window.ConstructorioSdkLoader?.injectSdk?.(),requestAnimationFrame(()=>{this.#searchInput.focus(),this.#syncDyRecommendationsVisibility(),this.#searchInput.value.trim()||(this.#renderRecentSearches(),this.fetchPopularSearchTerms())})};#handleModalClick=event=>{const target=event.target;target instanceof HTMLButtonElement||target instanceof HTMLAnchorElement||target instanceof HTMLInputElement||target.closest("button")||target.closest("a")||target.closest("input")||this.#searchInput.focus()};#handleSuggestionClick=event=>{const suggestionItem=event.target.closest(".cnstrc-predictive-search__suggestion-item");if(!suggestionItem||!this.contains(suggestionItem))return;const query=suggestionItem.dataset.query||"";query&&(this.#addToRecentSearches(query),window.location.href=this.#buildSearchUrl(query))};#handleResultsClick=event=>{const button=event.target.closest?.(".quick-add__button");if(!button||!this.contains(button))return;const quickAdd=button.closest?.("quick-add-component")??null;if(!quickAdd?.handleClick)return;const dialog=this.dialog,nativeDialog=this.closest?.("dialog")??null,closeDialog=dialog?.closeDialog;nativeDialog?.open===!0&&typeof closeDialog=="function"?(event.preventDefault(),event.stopPropagation(),closeDialog().then(()=>{requestAnimationFrame(()=>{quickAdd.handleClick?.(event)})})):quickAdd.handleClick(event)};#handleSuggestionPointerEnter=event=>{const item=event.target.closest(".cnstrc-predictive-search__suggestion-item");if(!item||!this.contains(item)||item.closest("[data-cnstrc-popular-searches]"))return;const suggestionItem=item,query=suggestionItem.dataset.query?.trim()||"";if(!query)return;const resultCount=Number.parseInt(suggestionItem.dataset.resultCount||"0",10)||0,cacheKey=this.#toCacheKey(query);this.#hoverIntentSuggestion=cacheKey,cacheKey!==this.#hoveredSuggestion&&(this.#setSuggestionActive(cacheKey),this.#queueHoverPrefetch(query,resultCount),this.#queueHoverSuggestion(query,resultCount))};#handleComponentPointerLeave=event=>{event.target===this&&(this.#queueHoverSuggestion.cancel(),this.#queueHoverPrefetch.cancel(),this.#hoverIntentSuggestion="",this.#hoverAbortController&&(this.#hoverAbortController.abort(),this.#hoverAbortController=null),this.#hoveredSuggestion&&this.#revertToDefaultProducts())};#revertToDefaultProducts(){this.#hoveredSuggestion="",this.#setSuggestionActive("");const currentCache=this.#suggestionCache.get(this.#activeProductsCacheKey());currentCache&&this.#renderProductsPanel({query:currentCache.query,products:currentCache.products,signal:null})}async fetchPopularSearchTerms(){try{const signal=this.#controller.signal,client=await this.#waitForConstructorClient(signal);if(!client?.recommendations?.getRecommendations||signal.aborted)return this.#popularSearchTerms=[],this.#popularSearchResultId="",this.#popularSearchNumResults=0,[];
1const response=await client.recommendations.getRecommendations("popular-searches",{section:"Search Suggestions"});if(signal.aborted)return[];const recommendationPayload=response??{},terms=this.#normalizePopularSearchTerms(response).slice(0,POPULAR_SEARCH_TERMS_LIMIT);return this.#popularSearchTerms=terms,this.#popularSearchResultId=recommendationPayload?.result_id||"",this.#popularSearchNumResults=recommendationPayload?.response?.total_num_results||0,this.#searchInput.value.trim()?this.#renderPopularSearchesSection():this.#renderRecentSearches(),terms}catch(error){return error instanceof Error&&error.name==="AbortError"?[]:(this.#popularSearchTerms=[],this.#popularSearchResultId="",this.#popularSearchNumResults=0,this.#searchInput.value.trim()&&this.#renderPopularSearchesSection(),[])}}async#performSearch(query){this.#currentQuery=query,this.#primarySuggestionQuery="",this.#hoveredSuggestion="",this.#hoverIntentSuggestion="",this.#queueHoverSuggestion.cancel(),this.#queueHoverPrefetch.cancel(),this.#setSuggestionActive(""),this.#showLoadingState();const signal=this.#createSearchAbortController().signal;this.#hoverAbortController&&(this.#hoverAbortController.abort(),this.#hoverAbortController=null);try{const client=await this.#waitForConstructorClient(signal);if(!client?.autocomplete?.getAutocompleteResults){this.#showErrorState();return}const response=await client.autocomplete.getAutocompleteResults(query,this.#buildAutocompleteOptions());if(signal.aborted||query!==this.#currentQuery)return;const parsed=normalizeAutocompleteResponse(response,query,this.#productFetchCap(),searchQuery=>this.#buildSearchUrl(searchQuery)),hasPredictiveResults=parsed.products.length>0||parsed.totalResults>0;this.#setSuggestions(parsed.suggestions,query,!hasPredictiveResults);const cacheKey=this.#toCacheKey(query);this.#suggestionCache.set(cacheKey,{query,products:parsed.products,totalResults:parsed.totalResults,fetchedAt:Date.now()});const primarySuggestionQuery=this.#topSuggestionQuery(parsed.suggestions)||query;this.#primarySuggestionQuery=primarySuggestionQuery,await this.#renderPrimaryProductsPanel({sourceQuery:query,primaryQuery:primarySuggestionQuery,fallbackProducts:parsed.products,fallbackTotalResults:parsed.totalResults,client,signal}),this.#prefetchTopSuggestions(parsed.suggestions)}catch(error){if(error instanceof Error&&error.name==="AbortError"||signal.aborted||query!==this.#currentQuery)return;this.#showErrorState()}}#topSuggestionQuery(suggestions){return!Array.isArray(suggestions)||!suggestions.length?"":suggestions[0]?.text?.trim()||""}#defaultProductsQuery(){return this.#primarySuggestionQuery||this.#currentQuery}#activeProductsCacheKey(){if(this.#hoveredSuggestion)return this.#hoveredSuggestion;const defaultQuery=this.#defaultProductsQuery();return defaultQuery?this.#toCacheKey(defaultQuery):""}async#renderPrimaryProductsPanel({sourceQuery,primaryQuery,fallbackProducts,fallbackTotalResults,client,signal}){const normalizedPrimaryQuery=primaryQuery.trim();if(!normalizedPrimaryQuery){this.#primarySuggestionQuery=sourceQuery,this.#renderProductsPanel({query:sourceQuery,products:fallbackProducts,signal});return}const primaryCacheKey=this.#toCacheKey(normalizedPrimaryQuery),sourceCacheKey=this.#toCacheKey(sourceQuery),cachedPrimary=this.#suggestionCache.get(primaryCacheKey);if(cachedPrimary){this.#renderProductsPanel({query:cachedPrimary.query,products:cachedPrimary.products,signal});return}if(primaryCacheKey===sourceCacheKey){this.#primarySuggestionQuery=normalizedPrimaryQuery,this.#renderProductsPanel({query:normalizedPrimaryQuery,products:fallbackProducts,signal});return}try{const response=await client.autocomplete.getAutocompleteResults(normalizedPrimaryQuery,this.#buildAutocompleteOptions());if(signal.aborted||sourceQuery!==this.#currentQuery)return;const parsedPrimary=normalizeAutocompleteResponse(response,normalizedPrimaryQuery,this.#productFetchCap(),searchQuery=>this.#buildSearchUrl(searchQuery));this.#suggestionCache.set(primaryCacheKey,{query:normalizedPrimaryQuery,products:parsedPrimary.products,totalResults:parsedPrimary.totalResults||fallbackTotalResults,fetchedAt:Date.now()}),this.#renderProductsPanel({query:normalizedPrimaryQuery,products:parsedPrimary.products,signal})}
1catch(error){if(error instanceof Error&&error.name==="AbortError"||signal.aborted||sourceQuery!==this.#currentQuery)return;this.#primarySuggestionQuery=sourceQuery,this.#renderProductsPanel({query:sourceQuery,products:fallbackProducts,signal})}}#prefetchTopSuggestions(suggestions){if(!Array.isArray(suggestions)||!suggestions.length||!this.#currentQuery)return;const fallbackResultCount=this.#suggestionCache.get(this.#toCacheKey(this.#currentQuery))?.totalResults||0,uniqueTopSuggestions=[],seen=new Set;for(const suggestion of suggestions){const query=suggestion?.text?.trim()||"";if(!query)continue;const cacheKey=this.#toCacheKey(query);if(!(!cacheKey||seen.has(cacheKey))&&(seen.add(cacheKey),uniqueTopSuggestions.push({query,resultCount:Number.isFinite(suggestion?.resultCount)?suggestion.resultCount:fallbackResultCount}),uniqueTopSuggestions.length>=HOVER_PREFETCH_LIMIT))break}if(!uniqueTopSuggestions.length)return;const prefetchWork=()=>{uniqueTopSuggestions.forEach(({query,resultCount})=>{this.#prefetchSuggestionProducts(query,resultCount)})};if("requestIdleCallback"in globalThis){globalThis.requestIdleCallback(prefetchWork,{timeout:400});return}setTimeout(prefetchWork,0)}async#prefetchSuggestionProducts(query,resultCount,{warmCards=!1}={}){const queryData=await this.#fetchSuggestionQueryData({query,resultCount,signal:this.#controller.signal});warmCards&&this.#warmBatchCards(queryData)}#warmBatchCards(queryData){if(!queryData||this.#controller.signal.aborted)return;const handles=queryData.products.map(product=>extractProductHandle(product)).filter(handle=>typeof handle=="string"&&handle.length>0);handles.length&&this.#fetchBatchCards(handles,this.#controller.signal).catch(()=>{})}async#fetchSuggestionQueryData({query,resultCount,signal}){const trimmed=query.trim();if(!trimmed||!this.#currentQuery)return null;const cacheKey=this.#toCacheKey(trimmed),cached=this.#suggestionCache.get(cacheKey);if(cached)return cached;const existingRequest=this.#suggestionRequests.get(cacheKey);if(existingRequest)return existingRequest;const requestPromise=(async()=>{try{if(signal?.aborted)return null;const client=await this.#waitForConstructorClient(signal??this.#controller.signal);if(!client?.autocomplete?.getAutocompleteResults||signal?.aborted)return null;const response=await client.autocomplete.getAutocompleteResults(trimmed,this.#buildAutocompleteOptions());if(signal?.aborted)return null;const parsed=normalizeAutocompleteResponse(response,trimmed,this.#productFetchCap(),searchQuery=>this.#buildSearchUrl(searchQuery)),queryData={query:trimmed,products:parsed.products,totalResults:parsed.totalResults||resultCount,fetchedAt:Date.now()};return this.#suggestionCache.set(cacheKey,queryData),queryData}catch(error){return error instanceof Error&&error.name==="AbortError",null}finally{this.#suggestionRequests.delete(cacheKey)}})();return this.#suggestionRequests.set(cacheKey,requestPromise),requestPromise}async#loadSuggestionProducts(query,resultCount){const trimmed=query.trim();if(!trimmed||!this.#currentQuery)return;const cacheKey=this.#toCacheKey(trimmed);if(cacheKey===this.#hoveredSuggestion||cacheKey!==this.#hoverIntentSuggestion)return;const signal=this.#createHoverAbortController().signal,cached=this.#suggestionCache.get(cacheKey);if(this.#hoveredSuggestion=cacheKey,this.#setSuggestionActive(cacheKey),cached&&this.#hasWarmCards(cached)){this.#renderProductsPanel({query:cached.query,products:cached.products,signal});return}this.#renderProductsPanel({query:trimmed,products:[],signal,loading:!0});const queryData=cached??await this.#fetchSuggestionQueryData({query:trimmed,resultCount,signal:null});if(!(signal.aborted||cacheKey!==this.#hoverIntentSuggestion)){if(!queryData){this.#revertToDefaultProducts();return}this.#renderProductsPanel({query:queryData.query,products:queryData.products,signal})}}#hasWarmCards(queryData){const handles=queryData.products.map(product=>extractProductHandle(product)).filter(handle=>typeof handle=="string"&&handle.length>0);return handles.length?hasBatchCards({handles,searchRoute:Theme.routes.search_url,sectionId:DEFAULT_BATCH_PREDICTIVE_SECTION_ID}):!1}async#waitForConstructorClient(signal){return waitForConstructorClient({signal,predicate:client=>!!client?.autocomplete?.getAutocompleteResults})}#buildAutocompleteOptions(){return buildAutocompleteOptions(this.#productFetchCap())}#productCap(){return this.#isDesktopProducts?DESKTOP_PRODUCT_LIMIT:MOBILE_PRODUCT_LIMIT}#productFetchCap(){return this.#isDesktopProducts?DESKTOP_PRODUCT_FETCH_LIMIT:MOBILE_PRODUCT_FETCH_LIMIT}#ensureRegions(){const container=this.#resultsContainer;let suggestions=container.querySelector(':scope > [data-cnstrc-region="suggestions"]'),products=container.querySelector(':scope > [data-cnstrc-region="products"]'),extras=container.querySelector(':scope > [data-cnstrc-region="extras"]');return(!suggestions||!products||!extras)&&(container.replaceChildren(),suggestions=this.#createRegion("suggestions"),products=this.#createRegion("products"),extras=this.#createRegion("extras"),container.append(suggestions,products,extras)),{suggestions,products,extras}}#createRegion(name){const region=document.createElement("div");
1return region.className="cnstrc-predictive-search__region",region.dataset.cnstrcRegion=name,region}#setSuggestions(suggestions,query,showNoResults=!1){const{suggestions:container}=this.#ensureRegions();container.removeAttribute("aria-busy");const t=this.#getTranslations();if(!showNoResults&&suggestions.length===0){container.innerHTML="",this.#renderPopularSearchesSection();return}const title=escapeHtml(t.topSuggestions||"Top Suggestions"),noResultsTemplate=this.#decodeTranslationHtmlEntities(t.noResults||""),noResultsText=escapeHtml(this.#interpolateQueryTranslation(noResultsTemplate,query)),noResultsHint=escapeHtml(this.#decodeTranslationHtmlEntities(t.noResultsHint||"")),highlightRegex=createHighlightRegex(query);container.innerHTML=suggestionsMarkup({title,suggestions:showNoResults?[]:suggestions,noResultsText,noResultsHint,activeCacheKey:this.#hoveredSuggestion,highlightRegex,toCacheKey:value=>this.#toCacheKey(value),formatSuggestionCount:count=>this.#formatSuggestionCount(count)}),this.#renderPopularSearchesSection()}#renderPopularSearchesSection(){const{extras}=this.#ensureRegions();extras.querySelector(".cnstrc-predictive-search__recent")?.remove(),extras.querySelector("[data-cnstrc-popular-searches]")?.remove(),this.#popularSearchTerms.length&&extras.insertAdjacentHTML("beforeend",this.#getPopularSearchesHtml(this.#popularSearchTerms))}async#renderProductsPanel({query,products,signal,loading=!1}){const panel=this.#productsPanel,t=this.#getTranslations(),title=t.relatedProducts||"Products",viewAllText=t.viewAll||"View all",searchUrl=this.#buildSearchUrl(query),shouldShowProductsPanel=loading||products.length>0,isDesktopProducts=this.#isDesktopProducts,skeletonHtml=productGridSkeletonHtml(this.#productCap());if(this.#setWithProductsPanel(isDesktopProducts&&shouldShowProductsPanel),!shouldShowProductsPanel){this.#removeInlineProducts();return}if(isDesktopProducts){this.#removeInlineProducts();const existingDesktopProducts=panel.querySelector("[data-cnstrc-products]");existingDesktopProducts instanceof HTMLElement?this.#updateProductsShell({productsContainer:existingDesktopProducts,title,query,searchUrl,viewAllText}):panel.innerHTML=productsPanelMarkup({title,query,searchUrl,viewAllText,skeletonHtml})}else this.#setInlineProducts({title,query,searchUrl,viewAllText,skeletonHtml});const injectEl=this.#cardsInjectElement(isDesktopProducts);if(!injectEl)return;if(loading){injectEl.innerHTML=skeletonHtml,injectEl.setAttribute("aria-busy","true");return}injectEl.setAttribute("aria-busy","true");const handles=products.map(product=>extractProductHandle(product)).filter(handle=>typeof handle=="string"&&handle.length>0);if(!handles.length){injectEl.replaceChildren(),injectEl.removeAttribute("aria-busy");return}try{const cardMap=await this.#fetchBatchCards(handles,signal);if(signal?.aborted)return;this.#replaceCardsContent({injectEl,cardMap,handles:handles.slice(0,this.#productCap())}),injectEl.removeAttribute("aria-busy"),injectEl.dispatchEvent(new CustomEvent("cnstrc:cards-initialized",{bubbles:!0,detail:{container:injectEl,query}}))}catch(error){if(error instanceof Error&&error.name==="AbortError")return;injectEl.replaceChildren(),injectEl.removeAttribute("aria-busy")}}#updateProductsShell({productsContainer,title,query,searchUrl,viewAllText}){productsContainer.dataset.cnstrcProductsQuery=query;const titleEl=productsContainer.querySelector("[data-cnstrc-products-title]");titleEl&&(titleEl.textContent=`${title} "${query}"`);const viewAllLink=productsContainer.querySelector("[data-cnstrc-products-view-all]");viewAllLink instanceof HTMLAnchorElement&&(viewAllLink.href=searchUrl,viewAllLink.textContent=viewAllText)}#cardsInjectElement(isDesktopProducts){const injectEl=isDesktopProducts?this.#productsPanel.querySelector("[data-cnstrc-cards-inject]"):this.#resultsContainer.querySelector("[data-cnstrc-products-inline] [data-cnstrc-cards-inject]");return injectEl instanceof HTMLElement?injectEl:null}#replaceCardsContent({injectEl,cardMap,handles}){const fragment=document.createDocumentFragment(),enteringCards=[];handles.forEach(handle=>{const cardFragment=getClonedCardFragment(cardMap,handle);if(cardFragment){const cardElement=cardFragment.querySelector("product-card");cardElement instanceof HTMLElement&&(cardElement.classList.add("cnstrc-predictive-search__card-enter"),enteringCards.push(cardElement)),fragment.appendChild(cardFragment)}}),injectEl.replaceChildren(fragment),!(!enteringCards.length||prefersReducedMotion())&&requestAnimationFrame(()=>{enteringCards.forEach(card=>{card.isConnected&&card.classList.add("is-visible")})})}#removeInlineProducts(){this.#resultsContainer.querySelector("[data-cnstrc-products-inline]")?.remove()}#setInlineProducts({title,query,searchUrl,viewAllText,skeletonHtml}){const{products}=this.#ensureRegions(),existingInlineProducts=products.querySelector("[data-cnstrc-products-inline]");if(existingInlineProducts instanceof HTMLElement){this.#updateProductsShell({productsContainer:existingInlineProducts,title,query,searchUrl,viewAllText});return}const markup=productsInlineMarkup({title,query,searchUrl,viewAllText,skeletonHtml});products.insertAdjacentHTML("beforeend",markup)}async#fetchBatchCards(handles,signal){const uniqueHandles=[...new Set(handles)];return uniqueHandles.length?fetchBatchCards({handles:uniqueHandles,signal,searchRoute:Theme.routes.search_url,sectionId:DEFAULT_BATCH_PREDICTIVE_SECTION_ID}):new Map}#showLoadingState(){const{suggestions}=this.#ensureRegions();suggestions.querySelector("[data-cnstrc-suggestions], [data-cnstrc-no-results]")?suggestions.setAttribute("aria-busy","true"):suggestions.innerHTML=`
2        <div class="cnstrc-predictive-search__loading">
3          <div class="cnstrc-predictive-search__loading-spinner"></div>
4        </div>
5      `,this.#renderPopularSearchesSection(),this.#renderProductsPanel({query:this.#defaultProductsQuery()||this.#currentQuery,products:[],signal:null,loading:!0})}#showErrorState(){const{suggestions}=this.#ensureRegions();suggestions.removeAttribute("aria-busy"),this.#setWithProductsPanel(!1),this.#removeInlineProducts();const msg=escapeHtml(this.#getTranslations().errorMessage||"");suggestions.innerHTML=`
6      <div class="cnstrc-predictive-search__error">
7        <p>${msg}</p>
8      </div>
9    `,this.#renderPopularSearchesSection()}#hideResetButton(){this.#resetButtonEl.hidden=!0}#showResetButton(){this.#resetButtonEl.hidden=!1}#syncDyRecommendationsVisibility(){const recommendationsEl=this.#dyRecommendations;recommendationsEl&&(recommendationsEl.hidden=this.#searchInput.value.trim().length>0)}resetSearch=debounce(()=>{this.#resetSearch()},100);#resetSearch(){this.#currentIndex=-1,this.#currentQuery="",this.#primarySuggestionQuery="",this.#hoveredSuggestion="",this.#hoverIntentSuggestion="",this.#suggestionCache.clear(),this.#suggestionRequests.clear(),this.#searchAbortController&&(this.#searchAbortController.abort(),this.#searchAbortController=null),this.#hoverAbortController&&(this.#hoverAbortController.abort(),this.#hoverAbortController=null),this.#queueHoverSuggestion.cancel(),this.#queueHoverPrefetch.cancel(),this.#searchInput.value="",this.#hideResetButton(),this.#syncDyRecommendationsVisibility(),this.#setWithProductsPanel(!1),this.#resultsContainer.innerHTML="",this.#renderRecentSearches()}#getRecentSearches(){try{const raw=localStorage.getItem(PredictiveSearchConstructorComponent.#RECENT_STORAGE_KEY);if(!raw)return[];const parsed=JSON.parse(raw);if(!Array.isArray(parsed))return[];const seen=new Set;return parsed.filter(item=>typeof item=="string").map(item=>item.trim()).filter(item=>!item||seen.has(item)?!1:(seen.add(item),!0)).slice(0,PredictiveSearchConstructorComponent.#RECENT_MAX)}catch{return[]}}#setRecentSearches(terms){try{localStorage.setItem(PredictiveSearchConstructorComponent.#RECENT_STORAGE_KEY,JSON.stringify(terms))}catch{}}#addToRecentSearches(term){const trimmed=term.trim();if(!trimmed)return;const list=[trimmed,...this.#getRecentSearches().filter(item=>item!==trimmed)].slice(0,PredictiveSearchConstructorComponent.#RECENT_MAX);this.#setRecentSearches(list)}#removeRecentSearchAt(index){if(!Number.isInteger(index)||index<0)return;const next=this.#getRecentSearches().filter((_,itemIndex)=>itemIndex!==index);this.#setRecentSearches(next)}#clearRecentSearches(){this.#setRecentSearches([])}#recentSearchesContainer(){return this.#ensureRegions().extras}
9#getRecentSearchesHtml(terms){if(!terms?.length)return"";const t=this.#getTranslations(),title=t.recentSearches??"Recent Searches",clearAllText=t.clearAll??"Clear all";return`
10        <div class="cnstrc-predictive-search__recent" aria-label="${escapeHtml(title)}">
11          <div class="cnstrc-predictive-search__recent-header">
12            <h3 class="cnstrc-predictive-search__section-title h2">${escapeHtml(title)}</h3>
13            <button
14              type="button"
15              class="cnstrc-predictive-search__recent-clear-all text-link"
16              data-cnstrc-recent-action="clear-all"
17              aria-label="${escapeHtml(t.clearAllRecentAria||clearAllText)}"
18            >
19              <span class="animated-underline">${escapeHtml(clearAllText)}</span>
20            </button>
21          </div>
22          <ul class="cnstrc-predictive-search__recent-list" role="listbox">
23            ${terms.map((term,index)=>{const safeQuery=escapeHtml(term),safeUrl=this.#buildSearchUrl(term),removeLabel=(t.removeFromRecent||"").replace(/\{\{\s*term\s*\}\}/g,term).trim();return`
24              <li
25                class="cnstrc-predictive-search__recent-item"
26                role="option"
27                data-query="${safeQuery}"
28                data-index="${index}"
29              >
30                <a href="${safeUrl}" class="cnstrc-predictive-search__recent-link">${safeQuery}</a>
31                <button
32                  type="button"
33                  class="cnstrc-predictive-search__recent-clear"
34                  data-cnstrc-recent-action="remove"
35                  data-cnstrc-index="${index}"
36                  aria-label="${escapeHtml(removeLabel||`Remove ${term} from recent searches`)}"
37                >
38                  <svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
39                  <rect width="25" height="25" rx="12.5" fill="#F0F0F0"/>
40                  <rect width="25" height="25" rx="12.5" fill="white" fill-opacity="0.85"/>
41                  <path d="M8 8L17.5 17.5" stroke="black"/>
42                  <path d="M17.5 8L8 17.5" stroke="black"/>
43                  </svg>
44                </button>
45              </li>
46            `}).join("")}
47          </ul>
48        </div>
49      `}#renderRecentSearches(){const container=this.#recentSearchesContainer(),recentSearchTerms=this.#getRecentSearches(),sections=[];recentSearchTerms.length&&sections.push(this.#getRecentSearchesHtml(recentSearchTerms)),this.#popularSearchTerms.length&&sections.push(this.#getPopularSearchesHtml(this.#popularSearchTerms)),container.innerHTML=sections.join("")}#normalizePopularSearchTerms(response){const data=response?.response??response??{},rawTerms=Array.isArray(data?.results)?data.results:Array.isArray(data?.recommendations)?data.recommendations:Array.isArray(data?.data?.results)?data.data.results:[],seen=new Set,normalizedTerms=[];for(const item of rawTerms){const term=this.#extractPopularSearchTerm(item);if(!term)continue;const key=this.#toCacheKey(term);!key||seen.has(key)||(seen.add(key),normalizedTerms.push(term))}return normalizedTerms}#extractPopularSearchTerm(item){if(typeof item=="string")return item.trim();if(!item||typeof item!="object")return"";const value=item?.value??item?.text??item?.query??item?.term??item?.data?.value??item?.data?.text??item?.data?.query??item?.data?.searchTerm??item?.data?.search_term??item?.data?.name??"";return typeof value=="string"?value.trim():""}#getPopularSearchesHtml(terms){if(!terms.length)return"";const t=this.#getTranslations(),title=t.popularSearches||t.topSuggestions||"Popular Searches";return`
50      <div class="cnstrc-predictive-search__trending-container
51            cnstrc-predictive-search__popular-container"
52            data-cnstrc-recommendations
53            data-cnstrc-recommendations-pod-id="popular-searches"
54            data-cnstrc-popular-searches
55            data-cnstrc-result-id="${this.#popularSearchResultId}"
56            data-cnstrc-num-results="${this.#popularSearchNumResults}"
57          >
58        <div class="cnstrc-predictive-search__suggestions cnstrc-predictive-search__trending">
59          <h3 class="cnstrc-predictive-search__section-title h2">${escapeHtml(title)}</h3>
60          <ul class="cnstrc-predictive-search__suggestions-list" role="listbox">
61            ${terms.map((term,index)=>{const safeTerm=escapeHtml(term),cacheKey=this.#toCacheKey(term);return`
62              <li
63                class="cnstrc-predictive-search__suggestion-item"
64                role="option"
65                data-query="${safeTerm}"
66                data-cache-key="${cacheKey}"
67                data-cnstrc-popular-item="true"
68                data-result-count="0"
69                data-index="${index}"
70                aria-selected="false"
71                data-cnstrc-item="recommendation"
72                data-cnstrc-item-id="${safeTerm}"
73                data-cnstrc-item-name="${safeTerm}"
74                data-cnstrc-item-section="Search Suggestions"
75                data-cnstrc-strategy-id="bestsellers"
76              >
77                <a
78                  href="${this.#buildSearchUrl(term)}"
79                  class="cnstrc-predictive-search__suggestion-link"
80                >
81                  ${safeTerm}
82                </a>
83              </li>
84            `}).join("")}
85          </ul>
86        </div>
87      </div>
88    `}#handleRecentSearchesClick=event=>{const actionButton=event.target.closest?.("[data-cnstrc-recent-action]");if(!(actionButton instanceof HTMLElement)||!this.contains(actionButton))return;const action=actionButton.dataset.cnstrcRecentAction;if(action){if(event.preventDefault(),event.stopPropagation(),action==="clear-all"){this.#clearRecentSearches(),this.#renderRecentSearches();return}if(action==="remove"){const index=Number.parseInt(actionButton.dataset.cnstrc
88Index||"-1",10);this.#removeRecentSearchAt(index),this.#renderRecentSearches()}}};#setWithProductsPanel(withProducts){const dialog=this.closest(".search-modal__content")||this.closest("dialog"),panel=this.#productsPanel;if(withProducts){dialog?.classList.add("search-modal__content--with-products"),panel.removeAttribute("hidden"),panel.setAttribute("data-visible","");return}dialog?.classList.remove("search-modal__content--with-products"),panel.removeAttribute("data-visible"),panel.setAttribute("hidden",""),panel.innerHTML=""}#buildSearchUrl(query){const searchUrl=new URL(Theme.routes.search_url,window.location.origin);return searchUrl.searchParams.set("q",query),searchUrl.toString()}#interpolateQueryTranslation(template,query){return template.replace(/\{\{\s*(terms|query)\s*\}\}/g,query)}#decodeTranslationHtmlEntities(value){const parser=document.createElement("textarea");return parser.innerHTML=value,parser.value}#formatSuggestionCount(count){const template=this.#getTranslations().resultsCountLabel||"{{ count }} results";return escapeHtml(formatSuggestionCount(template,count))}#toCacheKey(value){return toCacheKey(value)}#setSuggestionActive(activeCacheKey){this.querySelectorAll(".cnstrc-predictive-search__suggestion-item").forEach(item=>{const suggestionItem=item,isActive=suggestionItem.dataset.cacheKey===activeCacheKey;suggestionItem.setAttribute("aria-selected",isActive?"true":"false")})}#createSearchAbortController(){return this.#searchAbortController&&this.#searchAbortController.abort(),this.#searchAbortController=new AbortController,this.#searchAbortController}#createHoverAbortController(){return this.#hoverAbortController&&this.#hoverAbortController.abort(),this.#hoverAbortController=new AbortController,this.#hoverAbortController}}customElements.get("predictive-search-constructor-component")||customElements.define("predictive-search-constructor-component",PredictiveSearchConstructorComponent);export{PredictiveSearchConstructorComponent};
89//# sourceMappingURL=/cdn/shop/t/1989/assets/predictive-search-constructor.js.map?v=115204139741194717611790009629

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.