import type { CatalogItem } from '@tool.io/tools/compare';
import { matchesCatalogQuery } from './search';
export type CompareSuggestionGroup = {
items: CatalogItem[];
title:
| 'All items'
| 'Related items'
| 'Search results'
| 'Top comparisons';
};
const matchesQuery = (item: CatalogItem, needle: string) =>
matchesCatalogQuery(needle, [
item.name,
item.summary,
item.slug
]);
const alphabetically = (
first: CatalogItem,
second: CatalogItem
) => first.name.localeCompare(second.name, 'en-US');
const orderByPreferredSlugs = (
items: readonly CatalogItem[],
preferredSlugs: readonly string[]
) => {
const order = new Map(
preferredSlugs.map((slug, index) => [slug, index])
);
return [...items].sort(
(first, second) =>
(order.get(first.slug) ?? Number.MAX_SAFE_INTEGER) -
(order.get(second.slug) ?? Number.MAX_SAFE_INTEGER) ||
alphabetically(first, second)
);
};
export const groupCompareSuggestions = (
items: readonly CatalogItem[],
input: {
activeCategoryId: string | null;
excludedItemIds: readonly string[];
preferredRelatedSlugs?: readonly string[];
query: string;
topComparedSlugs: readonly string[];
}
): CompareSuggestionGroup[] => {
const needle = input.query.trim().toLocaleLowerCase('en-US');
const candidates = items.filter(
(item) => !input.excludedItemIds.includes(item.id)
);
if (needle) {
const results = candidates
.filter((item) => matchesQuery(item, needle))
.sort(alphabetically)
.slice(0, 12);
return results.length
? [{ items: results, title: 'Search results' }]
: [];
}
const topComparisons = orderByPreferredSlugs(
candidates.filter((item) =>
input.topComparedSlugs.includes(item.slug)
),
input.topComparedSlugs
);
if (input.activeCategoryId) {
const relatedItems = orderByPreferredSlugs(
candidates.filter((item) =>
item.categoryIds.includes(input.activeCategoryId!)
),
input.preferredRelatedSlugs ?? []
).slice(0, 12);
const unrelatedTopComparisons = topComparisons
.filter(
(item) =>
!item.categoryIds.includes(input.activeCategoryId!)
)
.slice(0, 6);
return [
{ items: relatedItems, title: 'Related items' as const },
{
items: unrelatedTopComparisons,
title: 'Top comparisons' as const
}
].filter((group) => group.items.length);
}
const topIds = new Set(topComparisons.map((item) => item.id));
const allItems = candidates
.filter((item) => !topIds.has(item.id))
.sort(alphabetically)
.slice(0, 12);
return [
{
items: topComparisons.slice(0, 8),
title: 'Top comparisons' as const
},
{ items: allItems, title: 'All items' as const }
].filter((group) => group.items.length);
};
export const filterCompareModalItems = (
items: readonly CatalogItem[],
input: {
activeCategoryId: string | null;
anchorItemId: string;
query: string;
}
): CatalogItem[] => {
const needle = input.query.trim().toLocaleLowerCase('en-US');
return items
.filter((item) => item.id !== input.anchorItemId)
.filter(
(item) =>
Boolean(needle) ||
!input.activeCategoryId ||
item.categoryIds.includes(input.activeCategoryId)
)
.filter(
(item) =>
!needle ||
`${item.name} ${item.summary} ${item.slug}`
.toLocaleLowerCase('en-US')
.includes(needle)
)
.sort((first, second) =>
first.name.localeCompare(second.name, 'en-US')
);
};
Browse the implementation files for Vehicles Comparison.