/* eslint-disable @typescript-eslint/restrict-plus-operands */ import { formatSearchParamsState, initSearchParamsState, removeSearchParam, setSearchParam, } from '@vtex/store-sdk' import { navigate } from 'gatsby' import React, { createContext, useMemo, useState } from 'react' import type { FC } from 'react' import type { SearchParamsState } from '@vtex/store-sdk' // TODO. I think we should remove data from this context once all componets are moved to the stores type SearchQuery = any export interface PageInfo { /** @description items per page */ size: number /** @description total number of pages */ total: number } interface PageConnection { cursor: number link: string } export interface SearchContext { pageInfo: PageInfo & { nextPage: PageConnection | false prevPage: PageConnection | false pages: number[] addNextPage: (e: any) => void addPreviousPage: (e: any) => void } data: SearchQuery searchParams: SearchParamsState setFacet: (item: Facet) => void toggleFacet: (item: Facet) => void toggleFacets: (item: Facet[]) => void setSort: (sort: SearchParamsState['sort']) => void } export const Context = createContext(undefined) Context.displayName = 'SearchContext' export const getLink = (searchParams: SearchParamsState) => { const { pathname, search } = formatSearchParamsState(searchParams) return `${pathname}${search}` } const apply = (params: SearchParamsState) => navigate(getLink(params)) interface Facet { selected?: boolean unique?: boolean value: string key: string } const toggleFacet = (item: Facet, state: SearchParamsState) => item.selected === true ? removeSearchParam(state, { unique: false, ...item }) : setSearchParam(state, { unique: false, ...item }) interface Props { data: SearchQuery searchParams: SearchParamsState pageInfo: PageInfo } export const SearchProvider: FC = ({ children, searchParams: initalState, data, pageInfo, }) => { const [pages, setPages] = useState([initalState.page]) const value = useMemo(() => { const paramsState = initSearchParamsState(initalState) const nextPage = pages[pages.length - 1] + 1 const previousPage = pages[0] - 1 const hasNextPage = pageInfo.total > pages[pages.length - 1] + 1 const hasPreviousPage = pages[0] > 0 const setPage = (e: any, direction: 'next' | 'prev') => { e.target.blur?.() e.preventDefault() if (direction === 'next' && hasNextPage) { setPages([...pages, nextPage]) } else if (direction === 'prev' && hasPreviousPage) { setPages([previousPage, ...pages]) } } return { toggleFacet: (item: Facet) => apply(toggleFacet(item, paramsState)), toggleFacets: (items: Facet[]) => apply(items.reduce((s, item) => toggleFacet(item, s), paramsState)), setFacet: (item: Facet) => apply(setSearchParam(paramsState, { unique: true, ...item })), setSort: (sort: SearchParamsState['sort']) => apply(setSearchParam(paramsState, { key: 'sort', value: sort })), searchParams: paramsState, data, pageInfo: { total: pageInfo.total, size: pageInfo.size, nextPage: hasNextPage && { cursor: nextPage, link: getLink({ ...paramsState, page: nextPage }), }, prevPage: hasPreviousPage && { cursor: previousPage, link: getLink({ ...paramsState, page: previousPage }), }, pages, addNextPage: (e: any) => setPage(e, 'next'), addPreviousPage: (e: any) => setPage(e, 'prev'), }, } }, [initalState, pages, pageInfo.total, pageInfo.size, data]) return {children} }