forked from Weaverse/pilot
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Weaverse#244 from Weaverse/dev
Update search page & predictive search, simplify structure, fix minor bugs
- Loading branch information
Showing
16 changed files
with
312 additions
and
537 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 91 additions & 56 deletions
147
app/components/layout/predictive-search/predictive-search-result.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,118 @@ | ||
import { Link } from "@remix-run/react"; | ||
import clsx from "clsx"; | ||
import type { | ||
NormalizedPredictiveSearchResultItem, | ||
NormalizedPredictiveSearchResults, | ||
SearchResultTypeProps, | ||
} from "~/types/predictive-search"; | ||
import { SearchResultItem } from "./result-item"; | ||
import { Image, Money } from "@shopify/hydrogen"; | ||
import type { MoneyV2 } from "@shopify/hydrogen/storefront-api-types"; | ||
import { Link } from "~/components/link"; | ||
import { CompareAtPrice } from "~/components/compare-at-price"; | ||
import { getImageAspectRatio, isDiscounted } from "~/lib/utils"; | ||
|
||
export function PredictiveSearchResult({ | ||
goToSearchResult, | ||
items, | ||
searchTerm, | ||
type, | ||
}: SearchResultTypeProps) { | ||
type SearchResultTypeProps = { | ||
items?: NormalizedPredictiveSearchResultItem[]; | ||
type: NormalizedPredictiveSearchResults[number]["type"]; | ||
}; | ||
|
||
export function PredictiveSearchResult({ items, type }: SearchResultTypeProps) { | ||
let isSuggestions = type === "queries"; | ||
let categoryUrl = `/search?q=${ | ||
searchTerm.current | ||
}&type=${pluralToSingularSearchType(type)}`; | ||
|
||
return ( | ||
<div | ||
key={type} | ||
className="predictive-search-result flex flex-col gap-4 divide-y divide-line-subtle" | ||
> | ||
<Link | ||
prefetch="intent" | ||
className="uppercase font-bold" | ||
to={categoryUrl} | ||
onClick={goToSearchResult} | ||
> | ||
<div key={type} className="predictive-search-result flex flex-col gap-4"> | ||
<div className="uppercase font-bold border-b border-line-subtle pb-3"> | ||
{isSuggestions ? "Suggestions" : type} | ||
</Link> | ||
{items?.length && ( | ||
</div> | ||
{items?.length ? ( | ||
<ul | ||
className={clsx( | ||
"pt-5", | ||
type === "queries" && "space-y-1", | ||
type === "articles" && "space-y-3", | ||
type === "products" && "space-y-4", | ||
)} | ||
> | ||
{items.map((item: NormalizedPredictiveSearchResultItem) => ( | ||
<SearchResultItem | ||
goToSearchResult={goToSearchResult} | ||
item={item} | ||
key={item.id} | ||
/> | ||
<SearchResultItem item={item} key={item.id} /> | ||
))} | ||
</ul> | ||
) : ( | ||
<div className="text-body-subtle"> | ||
No {isSuggestions ? "suggestions" : type} available. | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
/** | ||
* Converts a plural search type to a singular search type | ||
* | ||
* @example | ||
* ```js | ||
* pluralToSingularSearchType('articles'); // => 'ARTICLE' | ||
* pluralToSingularSearchType(['articles', 'products']); // => 'ARTICLE,PRODUCT' | ||
* ``` | ||
*/ | ||
function pluralToSingularSearchType( | ||
type: | ||
| NormalizedPredictiveSearchResults[number]["type"] | ||
| Array<NormalizedPredictiveSearchResults[number]["type"]>, | ||
) { | ||
let plural = { | ||
articles: "ARTICLE", | ||
collections: "COLLECTION", | ||
pages: "PAGE", | ||
products: "PRODUCT", | ||
queries: "QUERY", | ||
}; | ||
|
||
if (typeof type === "string") { | ||
return plural[type]; | ||
} | ||
type SearchResultItemProps = { | ||
item: NormalizedPredictiveSearchResultItem; | ||
}; | ||
|
||
return type.map((t) => plural[t]).join(","); | ||
function SearchResultItem({ | ||
item: { | ||
id, | ||
__typename, | ||
image, | ||
compareAtPrice, | ||
price, | ||
title, | ||
url, | ||
vendor, | ||
styledTitle, | ||
}, | ||
}: SearchResultItemProps) { | ||
return ( | ||
<li key={id}> | ||
<Link | ||
className="flex gap-4" | ||
to={ | ||
__typename === "SearchQuerySuggestion" || !url | ||
? `/search?q=${id}` | ||
: url | ||
} | ||
data-type={__typename} | ||
> | ||
{__typename === "Product" && ( | ||
<div className="h-20 w-20 shrink-0"> | ||
{image?.url && ( | ||
<Image | ||
alt={image.altText ?? ""} | ||
src={image.url} | ||
width={200} | ||
height={200} | ||
aspectRatio={getImageAspectRatio(image, "adapt")} | ||
className="h-full w-full object-cover object-center animate-fade-in" | ||
/> | ||
)} | ||
</div> | ||
)} | ||
<div className="space-y-1"> | ||
{vendor && ( | ||
<div className="text-body-subtle text-sm">By {vendor}</div> | ||
)} | ||
{styledTitle ? ( | ||
<div | ||
className="reveal-underline" | ||
dangerouslySetInnerHTML={{ __html: styledTitle }} | ||
/> | ||
) : ( | ||
<div | ||
className={clsx( | ||
__typename === "Product" ? "line-clamp-1" : "line-clamp-2", | ||
)} | ||
> | ||
<span className="reveal-underline">{title}</span> | ||
</div> | ||
)} | ||
{price && ( | ||
<div className="flex gap-2 text-sm"> | ||
<Money withoutTrailingZeros data={price as MoneyV2} /> | ||
{isDiscounted(price as MoneyV2, compareAtPrice as MoneyV2) && ( | ||
<CompareAtPrice data={compareAtPrice as MoneyV2} /> | ||
)} | ||
</div> | ||
)} | ||
</div> | ||
</Link> | ||
</li> | ||
); | ||
} |
Oops, something went wrong.