UNPKG

@progress/sitefinity-nextjs-sdk

Version:

Provides OOB widgets developed using the Next.js framework, which includes an abstraction layer for Sitefinity communication. Additionally, it offers an expanded API, typings, and tools for further development and integration.

220 lines (219 loc) 12.7 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useCallback, useEffect, useState } from 'react'; import { FacetGroup } from './components/facet-group'; import { RANGE_SEPARATOR, computeFacetRangeLabelForType, getCheckboxId, getFacetKeyFromCheckboxId } from './components/utils'; import { getFacets, getInitialFacetsWithModels, getSearchFacets, getSelectedFacetsToBeUsed, updateFacetsViewProps } from './search-facets-common'; import { useRouter, useSearchParams } from 'next/navigation'; import { getQueryParams, setQueryParams } from '../common/query-params'; const FILTER_QUERY_PARAM = 'filter'; export function SearchFacetsClient(viewProps) { const searchParamsNext = useSearchParams(); const router = useRouter(); const queryParams = getQueryParams(searchParamsNext); const [showClearButton, setShowClearButton] = useState(!!queryParams[FILTER_QUERY_PARAM]); const [sf, setSearchFacets] = useState([]); const [hasFacetElements, setHasAnyFacetElements] = useState(false); const [selectedFacets, setSelectedFacets] = useState({}); function setInitialSelectedFacets(sf) { const filterQuery = getQueryParams(searchParamsNext)[FILTER_QUERY_PARAM]; if (filterQuery) { const decodedFilterParam = atob(filterQuery); const jsonFilters = JSON.parse(decodedFilterParam); const newCheckedInputs = {}; jsonFilters.appliedFilters.forEach(function (filter) { filter.filterValues.forEach(function (fvObj) { const fieldName = decodeURIComponent(filter.fieldName); const filterValue = decodeURIComponent(fvObj.filterValue); let searchFacets = JSON.parse(JSON.stringify(sf)); let facetElement = searchFacets .find(x => x.FacetFieldName === fieldName)?.FacetElements .find(x => x.FacetValue === filterValue); let label = facetElement?.FacetLabel || ''; let mainValue = ''; if (!facetElement && fvObj.isCustom && filterValue.includes(RANGE_SEPARATOR)) { let facetField = searchFacets.find(x => x.FacetFieldName === fieldName); facetElement = facetField?.FacetElements .find(x => x.FacetValue?.includes(RANGE_SEPARATOR)); const [from, to] = filterValue.split(RANGE_SEPARATOR); label = facetField ? computeFacetRangeLabelForType(facetField.FacetFieldType, from, to) : ''; mainValue = facetElement?.FacetValue; } if (facetElement || searchFacets.find(x => x.FacetFieldName === fieldName && x.facetField?.FacetFieldSettings?.DisplayCustomRange)) { let inputId = getCheckboxId(fieldName, mainValue || filterValue); newCheckedInputs[inputId] = { facetLabel: label, facetName: fieldName, facetValue: filterValue, facetDefaultValue: mainValue || filterValue, isCustom: !!fvObj.isCustom }; } }); }); setSelectedFacets(newCheckedInputs); } } ; useEffect(() => { getInitialFacetsWithModels(queryParams, viewProps.widgetContext.model.Properties).then(({ searchFacets, hasAnyFacetElements }) => { setSearchFacets(searchFacets); setHasAnyFacetElements(hasAnyFacetElements); setInitialSelectedFacets(searchFacets); }); }, []); useEffect(() => { const newSearchParams = getQueryParams(searchParamsNext); if (!newSearchParams[FILTER_QUERY_PARAM]) { setSelectedFacets({}); // clear selected facets setShowClearButton(false); } const getNewSearchFacets = async () => { if (sf?.length) { const selectedFacetsToBeUsed = getSelectedFacetsToBeUsed(sf); const facets = getFacets(selectedFacetsToBeUsed, newSearchParams); const facetResponse = await getSearchFacets(newSearchParams.searchQuery, newSearchParams.sf_culture, newSearchParams.indexCatalogue, newSearchParams.filter, newSearchParams['resultsForAllSites'], null, facets); const { searchFacets, hasAnyFacetElements } = await updateFacetsViewProps(newSearchParams, facetResponse, selectedFacetsToBeUsed); setSearchFacets(searchFacets); setHasAnyFacetElements(hasAnyFacetElements); } else { getInitialFacetsWithModels(queryParams, viewProps.widgetContext.model.Properties).then(({ searchFacets, hasAnyFacetElements }) => { setSearchFacets(searchFacets); setHasAnyFacetElements(hasAnyFacetElements); }); } }; getNewSearchFacets(); }, [viewProps, searchParamsNext]); // eslint-disable-line const clearButtonClick = () => { setSelectedFacets({}); searchWithFilter(null, {}); }; const groupAllCheckedFacetInputs = useCallback((currentSelectedFacets) => { let groupedFilters = {}; Object.keys(currentSelectedFacets).sort().forEach((facetId) => { const selectedFacet = currentSelectedFacets[facetId]; const facetKey = selectedFacet.facetName; const filterValueObj = { filterValue: selectedFacet.facetValue, isCustom: selectedFacet.isCustom }; if (groupedFilters.hasOwnProperty(facetKey)) { groupedFilters[facetKey].push(filterValueObj); } else { groupedFilters[facetKey] = [filterValueObj]; } }); return groupedFilters; }, []); const buildFilterObjectBasedOnPopulatedInputs = useCallback((id, newSelectedFacets) => { let groupedFilters = groupAllCheckedFacetInputs(newSelectedFacets); let lastSelectedElementKey; let isDeselected = false; if (id) { const eventTargetElement = selectedFacets[id]; lastSelectedElementKey = getFacetKeyFromCheckboxId(id); isDeselected = !!eventTargetElement; } let filterObject = constructFilterObject(groupedFilters, lastSelectedElementKey, isDeselected); return filterObject; }, [groupAllCheckedFacetInputs, selectedFacets]); const searchWithFilter = useCallback((inputId, newSelectedFacets) => { const currentFilterObject = buildFilterObjectBasedOnPopulatedInputs(inputId, newSelectedFacets); let filterString = JSON.stringify(currentFilterObject); const newSearchParam = { ...queryParams }; delete newSearchParam['slug']; if (currentFilterObject && currentFilterObject.appliedFilters && currentFilterObject.appliedFilters.length > 0) { let encodedFilterString = btoa(filterString); newSearchParam[FILTER_QUERY_PARAM] = encodedFilterString; setShowClearButton(true); } else { delete newSearchParam[FILTER_QUERY_PARAM]; setShowClearButton(false); } let url = buildUrl(newSearchParam); router.push(url); // eslint-disable-next-line react-hooks/exhaustive-deps }, [queryParams, buildFilterObjectBasedOnPopulatedInputs]); function handleChipDeleteClick(facetKey, facetValue) { const newSelectedFacets = { ...selectedFacets }; delete newSelectedFacets[getCheckboxId(facetKey, facetValue)]; setSelectedFacets(newSelectedFacets); searchWithFilter(getCheckboxId(facetKey, facetValue), newSelectedFacets); } function buildUrl(queryStringParams) { let currentLocation = window.location.href.split('?')[0]; const url = new URL(currentLocation); // return the pager to 0 delete queryStringParams.page; url.search = setQueryParams(queryStringParams); return url.toString(); } function constructFilterObject(groupedFilters, lastSelectedElementKey, isDeselected) { const currentFilterObject = { appliedFilters: Object.keys(groupedFilters).map((el) => { return { fieldName: encodeURIComponent(el), filterValues: groupedFilters[el].map((filter) => ({ ...filter, filterValue: encodeURIComponent(filter.filterValue) })) }; }), lastSelectedFilterGropName: lastSelectedElementKey, isDeselected: isDeselected }; return currentFilterObject; } const facetValueChanged = (facetName, facetValue, facetDefaultValue, facetLabel, isDeselected, isCustom, newSelectedFacets) => { const checkboxId = getCheckboxId(facetName, facetValue); const currentSelectedFacets = newSelectedFacets !== null ? newSelectedFacets : JSON.parse(JSON.stringify(selectedFacets)); const existingCustomSelectedFacets = Object.values(currentSelectedFacets).filter(f => (f.facetName === facetName) && f.isCustom); if (!isCustom && existingCustomSelectedFacets.length > 0) { existingCustomSelectedFacets.forEach((f) => { const id = getCheckboxId(f.facetName, f.facetValue); delete currentSelectedFacets[id]; }); } if (isDeselected) { delete currentSelectedFacets[checkboxId]; } else { currentSelectedFacets[checkboxId] = { facetName, facetLabel, facetValue, facetDefaultValue, isCustom }; } setSelectedFacets(currentSelectedFacets); searchWithFilter(checkboxId, currentSelectedFacets); }; const deselectFacetGroup = (facetName) => { let selectedFacetsClone = JSON.parse(JSON.stringify(selectedFacets)); let newSelectedFacets = removeGroup(facetName, selectedFacetsClone); return newSelectedFacets; }; const removeGroup = (facetName, newSelectedFacets) => { const newFacets = {}; Object.keys(newSelectedFacets).forEach(key => { const value = newSelectedFacets[key]; if (facetName !== value.facetName) { newFacets[key] = value; } }); return newFacets; }; return (_jsxs(_Fragment, { children: [(hasFacetElements || (!!viewProps.indexCatalogue && viewProps.isEdit) || !!Object.keys(selectedFacets).length) && (_jsxs(_Fragment, { children: [_jsx("h3", { className: "h6 mb-3 fw-normal", children: viewProps.filterResultsLabel }), (hasFacetElements || !!Object.keys(selectedFacets).length) && _jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center justify-content-between", children: [_jsx("label", { className: "form-label", children: viewProps.appliedFiltersLabel }), showClearButton && _jsx("button", { onClick: clearButtonClick, id: "sf-facet-clear-all-btn", className: "btn btn-link px-0 py-0 mb-2 text-decoration-none", children: viewProps.clearAllLabel })] }), _jsx("ul", { id: "applied-filters", className: "list-unstyled list-inline", children: Object.entries(selectedFacets).map(([_, value], idx) => { const { facetName, facetLabel, facetValue, facetDefaultValue } = value; return value && _jsxs("li", { className: 'list-inline-item bg-secondary bg-opacity-10 rounded-pill ps-2 pe-4 pb-1 me-1 mb-1 mw-100 position-relative overflow-hidden text-truncate text-nowrap', children: [facetLabel, _jsx("span", { onClick: () => handleChipDeleteClick(facetName, facetDefaultValue), id: `remove-facet-filter-${facetName}-${facetValue}`, role: "button", tabIndex: 0, title: "Remove", className: "px-2 position-absolute end-0", children: "\u2715" })] }, idx); }) })] })] })), sf && _jsx("div", { id: "facetContainer", className: "mb-3", children: sf.map((facet, sfIdx) => { return _jsx(FacetGroup, { viewProps: viewProps, facet: facet, facetValueChanged: facetValueChanged, deselectFacetGroup: deselectFacetGroup, selectedFacets: selectedFacets }, sfIdx); }) })] })); }