import { useTranslation } from 'react-i18next'; import { CloseIcon } from '../icons/CloseIcon'; import { AppliedFiltersCssClasses } from './AppliedFilters'; import { useClearFiltersCallback } from '../hooks/useClearFiltersCallback'; import { FieldValueFilter, useSearchActions } from '@yext/search-headless-react'; import { isDuplicateFieldValueFilter } from '../utils/filterutils'; import { executeSearch } from '../utils'; import React, { useCallback, useMemo } from 'react'; /** * A representation of a filter that can be removed from the AppliedFilters component. * * @internal */ export interface RemovableFilter { displayName: string, handleRemove: () => void, filter: FieldValueFilter } /** * Properties for {@link AppliedFilters}. * * @internal */ export interface AppliedFiltersDisplayProps { removableFilters?: RemovableFilter[], /** * The display values of filters that are applied to the search results * from the backend's natural language processing. */ nlpFilterDisplayNames?: string[], /** CSS classes for customizing the component styling. */ cssClasses?: AppliedFiltersCssClasses } /** * A component that renders applied filters based on the provided GroupedFilters. * * @param props - {@link AppliedFiltersDisplayProps} * @returns A React element for the applied filters */ export function AppliedFiltersDisplay(props: AppliedFiltersDisplayProps): React.JSX.Element | null { const { t } = useTranslation(); const { nlpFilterDisplayNames = [], removableFilters = [], cssClasses = {} } = props; const handleClickClearAllButton = useClearFiltersCallback(); const searchActions = useSearchActions(); const dedupedNlpFilterDisplayNames = nlpFilterDisplayNames.filter(displayName => { return !removableFilters.some(f => f.displayName === displayName); }); const dedupedRemovableFilters = getDedupedRemovableFilters(removableFilters); const handleRemoveDedupedFilter = useCallback((dedupedFilter: DedupedRemovableFilter) => { dedupedFilter.handleRemove(); for (const f of dedupedFilter.duplicates ?? []) { f.handleRemove(); } searchActions.setOffset(0); executeSearch(searchActions); }, [searchActions]); const removableFiltersWithHandlers = useMemo(() => { return dedupedRemovableFilters.map(filter => ({ filter, handleRemove: () => handleRemoveDedupedFilter(filter) })); }, [dedupedRemovableFilters, handleRemoveDedupedFilter]); if (removableFilters.length + nlpFilterDisplayNames.length === 0) { return null; } return (
{dedupedNlpFilterDisplayNames.map((displayName, i) => renderNlpFilter(displayName, i, cssClasses))} {removableFiltersWithHandlers.map(({ filter, handleRemove }, i) => { return ( ); })} {removableFilters.length > 0 && }
); } interface DedupedRemovableFilter extends RemovableFilter { duplicates?: RemovableFilter[] } function getDedupedRemovableFilters(filters: RemovableFilter[]) { const dedupedFilters: DedupedRemovableFilter[] = []; for (const f of filters) { const preexistingDupe = dedupedFilters.find(d => isDuplicateFieldValueFilter(d.filter, f.filter)); if (!preexistingDupe) { dedupedFilters.push(f); } else { if (!preexistingDupe.duplicates) { preexistingDupe.duplicates = [f]; } else { preexistingDupe.duplicates.push(f); } } } return dedupedFilters; } function RemovableFilter({ displayName, handleRemove, cssClasses }: { displayName: string | undefined, handleRemove: () => void, cssClasses: AppliedFiltersCssClasses }): React.JSX.Element { const { t } = useTranslation(); return (
{displayName}
); } function renderNlpFilter( displayName: string | undefined, index: number, cssClasses: AppliedFiltersCssClasses ): React.JSX.Element { return (
{displayName}
); }