Files
medusa-store/www/packages/docs-ui/src/providers/Search/index.tsx
T
Shahed Nasser b3f75d8f21 docs: add AI Assistant (#5249)
* added components

* added ai assistant button

* change styling

* improve AI assistant

* change to a drawer

* added command support into search

* add AiAssistant to all projects

* remove usage of Text component

* added error handling

* use recaptcha

* fix new configurations

* fix background color

* change suggested questions
2023-10-05 11:10:44 +03:00

174 lines
4.4 KiB
TypeScript

"use client"
import React, {
createContext,
useContext,
useEffect,
useState,
useMemo,
useRef,
} from "react"
import { BadgeProps, Modal, Search, SearchProps } from "@/components"
import { checkArraySameElms } from "../../utils"
import algoliasearch, { SearchClient } from "algoliasearch/lite"
import clsx from "clsx"
import { CSSTransition, SwitchTransition } from "react-transition-group"
export type SearchCommand = {
name: string
component: React.ReactNode
icon?: React.ReactNode
title: string
badge?: BadgeProps
}
export type SearchContextType = {
isOpen: boolean
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>
defaultFilters: string[]
setDefaultFilters: (value: string[]) => void
searchClient: SearchClient
commands: SearchCommand[]
command: SearchCommand | null
setCommand: React.Dispatch<React.SetStateAction<SearchCommand | null>>
modalRef: React.MutableRefObject<HTMLDialogElement | null>
}
const SearchContext = createContext<SearchContextType | null>(null)
export type AlgoliaProps = {
appId: string
apiKey: string
mainIndexName: string
indices: string[]
}
export type SearchProviderProps = {
children: React.ReactNode
initialDefaultFilters?: string[]
algolia: AlgoliaProps
searchProps: Omit<SearchProps, "algolia">
commands?: SearchCommand[]
modalClassName?: string
}
export const SearchProvider = ({
children,
initialDefaultFilters = [],
searchProps,
algolia,
commands = [],
modalClassName,
}: SearchProviderProps) => {
const [isOpen, setIsOpen] = useState(false)
const [defaultFilters, setDefaultFilters] = useState<string[]>(
initialDefaultFilters
)
const [command, setCommand] = useState<SearchCommand | null>(null)
const modalRef = useRef<HTMLDialogElement | null>(null)
const searchClient: SearchClient = useMemo(() => {
const algoliaClient = algoliasearch(algolia.appId, algolia.apiKey)
return {
...algoliaClient,
async search(requests) {
if (requests.every(({ params }) => !params?.query)) {
return Promise.resolve({
results: requests.map(() => ({
hits: [],
nbHits: 0,
nbPages: 0,
page: 0,
processingTimeMS: 0,
hitsPerPage: 0,
exhaustiveNbHits: false,
query: "",
params: "",
})),
})
}
return algoliaClient.search(requests)
},
}
}, [algolia.appId, algolia.apiKey])
useEffect(() => {
if (
initialDefaultFilters.length &&
!checkArraySameElms(defaultFilters, initialDefaultFilters)
) {
setDefaultFilters(initialDefaultFilters)
}
}, [initialDefaultFilters])
return (
<SearchContext.Provider
value={{
isOpen,
setIsOpen,
defaultFilters,
setDefaultFilters,
searchClient,
commands,
command,
setCommand,
modalRef,
}}
>
{children}
<Modal
contentClassName={clsx(
"!p-0 overflow-hidden relative h-full",
"rounded-none md:rounded-docs_lg flex flex-col justify-between"
)}
modalContainerClassName={clsx(
"!rounded-none md:!rounded-docs_lg",
"md:!h-[480px] h-screen",
"md:!w-[640px] w-screen",
"bg-medusa-bg-base"
)}
open={isOpen}
onClose={() => setIsOpen(false)}
passedRef={modalRef}
className={modalClassName}
>
<SwitchTransition>
<CSSTransition
classNames={{
enter:
command === null
? "animate-fadeInLeft animate-fast"
: "animate-fadeInRight animate-fast",
exit:
command === null
? "animate-fadeOutLeft animate-fast"
: "animate-fadeOutRight animate-fast",
}}
timeout={300}
key={command?.name || "search"}
>
<>
{command === null && (
<Search {...searchProps} algolia={algolia} />
)}
{command?.component}
</>
</CSSTransition>
</SwitchTransition>
</Modal>
</SearchContext.Provider>
)
}
export const useSearch = (): SearchContextType => {
const context = useContext(SearchContext)
if (!context) {
throw new Error("useSearch must be used inside a SearchProvider")
}
return context
}