"use client"; import { getPredictiveSearchItemUrl, type PredictiveSearchData, type PredictiveSearchState, } from "@shopify/hydrogen"; import { PredictiveSearchProvider, usePredictiveSearch, usePredictiveSearchActions, usePredictiveSearchForm, } from "@shopify/hydrogen/react"; import Link from "next/link"; import { useEffect, useRef } from "react"; import { content } from "@/lib/content"; import { shopifyImageUrl } from "@/lib/image"; import { formatPrice } from "@/lib/money"; import { routeTemplates } from "@/lib/route-templates"; const PREDICTIVE_SEARCH_LIMIT = 5; const PREDICTIVE_SEARCH_DIALOG_ID = "search-modal"; /** * Predictive search modal (`hydrogen-predictive-search` skill). Opened from the * header search trigger. The form is a native `GET /search` fallback so a no-JS * shopper reaches full search results (F4). The modal is a centered `` * (`.dialog-center`). Products only, per `notes/predictive-search.md`. */ export function PredictiveSearchModal({ isOpen, onClose, }: { isOpen: boolean; onClose: () => void; }) { return ( ); } function PredictiveSearchDialogInner({ isOpen, onClose, }: { isOpen: boolean; onClose: () => void; }) { const dialogRef = useRef(null); const state = usePredictiveSearch(); const { clear } = usePredictiveSearchActions(); const { formProps, register } = usePredictiveSearchForm(); // Open/close the native and clear predictive state on close so stale // suggestions do not reappear on the next open. useEffect(() => { const dialog = dialogRef.current; if (!dialog) return; if (isOpen && !dialog.open) { dialog.showModal(); } else if (!isOpen && dialog.open) { dialog.close(); clear(); } }, [isOpen, clear]); return (
{state.result.term || state.result.items.products.length > 0 ? (

{content.search.title}

) : (

{content.search.title}

)} {state.result.items.products.length > 0 ? (
View all results
) : null}
); } function PredictiveBody({ state, onNavigate, }: { state: PredictiveSearchState; onNavigate: () => void; }) { const { status, error, result } = state; const products = result.items.products; const hasResults = products.length > 0; const term = result.term; if (status === "loading") { return

Searching…

; } if (error) { return (

{error}

); } if (status === "success" && !hasResults && term) { return (

No results for “{term}”

); } if (!hasResults) return null; return (
    {products.map((product) => { const href = getPredictiveSearchItemUrl(product, { routes: routeTemplates, term }); const variant = product.selectedOrFirstAvailableVariant; const image = variant?.image ?? null; const price = variant?.price ?? null; return (
  • {image ? ( {image.altText ) : null} {product.title} {price ? ( {formatPrice(price)} ) : null}
  • ); })}
); }