+ {commands.length > 0 && (
+ <>
+
+ {commands.map((command, index) => (
+
setCommand(command)}
+ key={index}
+ tabIndex={index}
+ className="gap-docs_0.75"
+ >
+ <>
+ {command.icon}
+ {command.title}
+ {command.badge && }
+ >
+
+ ))}
+ >
+ )}
{suggestions.map((suggestion, index) => (
{suggestion.items.map((item, itemIndex) => (
-
setIndexUiState({
query: item,
})
}
key={itemIndex}
- tabIndex={itemIndex}
- data-hit
+ tabIndex={commands.length + itemIndex}
>
-
- {item}
-
-
+ {item}
+
))}
))}
diff --git a/www/packages/docs-ui/src/components/Search/index.tsx b/www/packages/docs-ui/src/components/Search/index.tsx
index 4bbc170203..2b631bf864 100644
--- a/www/packages/docs-ui/src/components/Search/index.tsx
+++ b/www/packages/docs-ui/src/components/Search/index.tsx
@@ -1,6 +1,215 @@
-export * from "./EmptyQueryBoundary"
-export * from "./Hits"
-export * from "./Modal"
-export * from "./ModalOpener"
-export * from "./NoResults"
-export * from "./Suggestions"
+"use client"
+
+import React, { useEffect, useMemo, useRef, useState } from "react"
+import { InstantSearch, SearchBox } from "react-instantsearch"
+import clsx from "clsx"
+import { SearchEmptyQueryBoundary } from "./EmptyQueryBoundary"
+import { SearchSuggestions, type SearchSuggestionType } from "./Suggestions"
+import { AlgoliaProps, useSearch } from "@/providers"
+import { checkArraySameElms } from "@/utils"
+import { SearchHitsWrapper } from "./Hits"
+import { Button, Kbd, SelectBadge } from "@/components"
+import { MagnifyingGlass, XMark } from "@medusajs/icons"
+import { useSearchNavigation, type OptionType } from "@/hooks"
+
+export type SearchProps = {
+ algolia: AlgoliaProps
+ isLoading?: boolean
+ suggestions: SearchSuggestionType[]
+ checkInternalPattern?: RegExp
+ filterOptions?: OptionType[]
+}
+
+export const Search = ({
+ algolia,
+ suggestions,
+ isLoading = false,
+ checkInternalPattern,
+ filterOptions = [],
+}: SearchProps) => {
+ const { isOpen, setIsOpen, defaultFilters, searchClient, modalRef } =
+ useSearch()
+ const [filters, setFilters] = useState
(defaultFilters)
+ const formattedFilters: string = useMemo(() => {
+ let formatted = ""
+ filters.forEach((filter) => {
+ const split = filter.split("_")
+ split.forEach((f) => {
+ if (formatted.length) {
+ formatted += " OR "
+ }
+ formatted += `_tags:${f}`
+ })
+ })
+ return formatted
+ }, [filters])
+ const searchBoxRef = useRef(null)
+
+ const focusSearchInput = () =>
+ searchBoxRef.current?.querySelector("input")?.focus()
+
+ useEffect(() => {
+ if (!checkArraySameElms(defaultFilters, filters)) {
+ setFilters(defaultFilters)
+ }
+ }, [defaultFilters])
+
+ useEffect(() => {
+ if (isOpen && searchBoxRef.current) {
+ focusSearchInput()
+ } else if (!isOpen) {
+ const focusedItem = modalRef.current?.querySelector(
+ ":focus"
+ ) as HTMLElement
+ if (
+ focusedItem &&
+ focusedItem === searchBoxRef.current?.querySelector("input")
+ ) {
+ // remove focus
+ focusedItem.blur()
+ }
+ }
+ }, [isOpen])
+
+ useSearchNavigation({
+ getInputElm: () =>
+ searchBoxRef.current?.querySelector("input") as HTMLInputElement,
+ focusInput: focusSearchInput,
+ keyboardProps: {
+ isLoading,
+ },
+ })
+
+ return (
+
+
+
+ (
+
+ )}
+ resetIconComponent={() => (
+
+ )}
+ placeholder="Find something..."
+ autoFocus
+ formRef={searchBoxRef}
+ />
+
+
+
+ }
+ >
+
+
+
+
+
+ {filterOptions.length && (
+
+ setFilters(Array.isArray(value) ? [...value] : [value])
+ }
+ addSelected={(value) => setFilters((prev) => [...prev, value])}
+ removeSelected={(value) =>
+ setFilters((prev) => prev.filter((v) => v !== value))
+ }
+ showClearButton={false}
+ placeholder="Filters"
+ handleAddAll={(isAllSelected: boolean) => {
+ if (isAllSelected) {
+ setFilters(defaultFilters)
+ } else {
+ setFilters(filterOptions.map((option) => option.value))
+ }
+ }}
+ />
+ )}
+
+
+
+ Navigation
+
+
+ ↑
+ ↓
+
+
+
+
+ Open Result
+
+ ↵
+
+
+
+
+ )
+}
diff --git a/www/packages/docs-ui/src/components/Select/Badge/index.tsx b/www/packages/docs-ui/src/components/Select/Badge/index.tsx
index ed16262919..3b77145f63 100644
--- a/www/packages/docs-ui/src/components/Select/Badge/index.tsx
+++ b/www/packages/docs-ui/src/components/Select/Badge/index.tsx
@@ -77,7 +77,7 @@ export const SelectBadge = ({
void
+}
+
+export const useCopy = (text: string): useCopyReturnType => {
+ const [isCopied, setIsCopied] = useState(false)
+ const copyTimeout = useRef
(0)
+
+ const handleCopy = useCallback(() => {
+ copy(text)
+ setIsCopied(true)
+ copyTimeout.current = window.setTimeout(() => {
+ setIsCopied(false)
+ }, 1000)
+ }, [text])
+
+ useEffect(() => () => window.clearTimeout(copyTimeout.current), [])
+
+ return { isCopied, handleCopy }
+}
diff --git a/www/packages/docs-ui/src/hooks/use-search-navigation/index.ts b/www/packages/docs-ui/src/hooks/use-search-navigation/index.ts
new file mode 100644
index 0000000000..110f2cbee8
--- /dev/null
+++ b/www/packages/docs-ui/src/hooks/use-search-navigation/index.ts
@@ -0,0 +1,144 @@
+"use client"
+
+import { useCallback, useEffect, useMemo } from "react"
+import { useSearch } from "@/providers"
+import { findNextSibling, findPrevSibling } from "@/utils"
+import {
+ useKeyboardShortcut,
+ type useKeyboardShortcutOptions,
+} from "../use-keyboard-shortcut"
+
+export type useSearchNavigationProps = {
+ getInputElm: () => HTMLInputElement | null
+ focusInput: () => void
+ handleSubmit?: () => void
+ keyboardProps?: Partial
+}
+
+export const useSearchNavigation = ({
+ getInputElm,
+ focusInput,
+ handleSubmit,
+ keyboardProps,
+}: useSearchNavigationProps) => {
+ const shortcutKeys = useMemo(() => ["ArrowUp", "ArrowDown", "Enter"], [])
+ const { modalRef, isOpen } = useSearch()
+
+ const handleKeyAction = (e: KeyboardEvent) => {
+ if (!isOpen) {
+ return
+ }
+ e.preventDefault()
+ const focusedItem = modalRef.current?.querySelector(":focus") as HTMLElement
+ if (!focusedItem) {
+ // focus the first data-hit
+ const nextItem = modalRef.current?.querySelector(
+ "[data-hit]"
+ ) as HTMLElement
+ nextItem?.focus()
+ return
+ }
+
+ const isHit = focusedItem.hasAttribute("data-hit")
+ const isInput = focusedItem.tagName.toLowerCase() === "input"
+
+ if (!isHit && !isInput) {
+ // ignore if focused items aren't input/data-hit
+ return
+ }
+
+ const lowerPressedKey = e.key.toLowerCase()
+
+ if (lowerPressedKey === "enter") {
+ if (isHit) {
+ // trigger click event of the focused element
+ focusedItem.click()
+ } else {
+ handleSubmit?.()
+ }
+ return
+ }
+
+ if (lowerPressedKey === "arrowup") {
+ // only hit items has action on arrow up
+ if (isHit) {
+ // find if there's a data-hit item before this one
+ const beforeItem = findPrevSibling(focusedItem, "[data-hit]")
+ if (!beforeItem) {
+ // focus the input
+ focusInput()
+ } else {
+ // focus the previous item
+ beforeItem.focus()
+ }
+ }
+ } else if (lowerPressedKey === "arrowdown") {
+ // check if item is input or hit
+ if (isInput) {
+ // go to the first data-hit item
+ const nextItem = modalRef.current?.querySelector(
+ "[data-hit]"
+ ) as HTMLElement
+ nextItem?.focus()
+ } else {
+ // handle go down for hit items
+ // find if there's a data-hit item after this one
+ const afterItem = findNextSibling(focusedItem, "[data-hit]")
+ if (afterItem) {
+ // focus the next item
+ afterItem.focus()
+ }
+ }
+ }
+ }
+
+ /** Handles starting to type which focuses the input */
+ const handleKeyDown = useCallback(
+ (e: KeyboardEvent) => {
+ if (!isOpen) {
+ return
+ }
+ // check if shortcut keys were pressed
+ const lowerPressedKey = e.key.toLowerCase()
+ const pressedShortcut = [...shortcutKeys, "Escape"].some(
+ (s) => s.toLowerCase() === lowerPressedKey
+ )
+ if (pressedShortcut) {
+ return
+ }
+
+ const focusedItem = modalRef.current?.querySelector(
+ ":focus"
+ ) as HTMLElement
+ const inputElm = getInputElm()
+ if (inputElm && focusedItem !== inputElm) {
+ focusInput()
+ }
+ },
+ [
+ shortcutKeys,
+ isOpen,
+ modalRef.current,
+ shortcutKeys,
+ getInputElm,
+ focusInput,
+ ]
+ )
+
+ useEffect(() => {
+ window.addEventListener("keydown", handleKeyDown)
+
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown)
+ }
+ }, [handleKeyDown])
+
+ useKeyboardShortcut({
+ metakey: false,
+ shortcutKeys: shortcutKeys,
+ checkEditing: false,
+ isLoading: false,
+ action: handleKeyAction,
+ ...keyboardProps,
+ })
+}
diff --git a/www/packages/docs-ui/src/providers/AiAssistant/index.tsx b/www/packages/docs-ui/src/providers/AiAssistant/index.tsx
new file mode 100644
index 0000000000..79ff129fd2
--- /dev/null
+++ b/www/packages/docs-ui/src/providers/AiAssistant/index.tsx
@@ -0,0 +1,120 @@
+"use client"
+
+import React, { createContext, useContext } from "react"
+import { useAnalytics } from "@/providers"
+import { AiAssistant } from "@/components"
+import ReCAPTCHA from "react-google-recaptcha"
+
+export type AiAssistantFeedbackType = "upvote" | "downvote"
+
+export type AiAssistantContextType = {
+ getAnswer: (question: string, thread_id?: string) => Promise
+ sendFeedback: (
+ questionId: string,
+ reaction: AiAssistantFeedbackType
+ ) => Promise
+}
+
+const AiAssistantContext = createContext(null)
+
+export type AiAssistantProviderProps = {
+ children?: React.ReactNode
+ apiUrl: string
+ recaptchaSiteKey: string
+ websiteId: string
+}
+
+export const AiAssistantProvider = ({
+ apiUrl,
+ recaptchaSiteKey,
+ websiteId,
+ children,
+}: AiAssistantProviderProps) => {
+ const { analytics } = useAnalytics()
+ const recaptchaRef = React.createRef()
+
+ const getReCaptchaToken = async () => {
+ if (recaptchaRef?.current) {
+ const recaptchaToken = await recaptchaRef.current.executeAsync()
+ return recaptchaToken || ""
+ }
+ return ""
+ }
+
+ const sendRequest = async (
+ apiPath: string,
+ method = "GET",
+ headers?: HeadersInit,
+ body?: BodyInit
+ ) => {
+ return await fetch(`${apiUrl}${apiPath}`, {
+ method,
+ headers: {
+ "X-RECAPTCHA-TOKEN": await getReCaptchaToken(),
+ "X-WEBSITE-ID": websiteId,
+ ...headers,
+ },
+ body,
+ })
+ }
+
+ const getAnswer = async (question: string, threadId?: string) => {
+ const questionParam = encodeURI(question)
+ return await sendRequest(
+ threadId
+ ? `/query/v1/thread/${threadId}/stream?query=${questionParam}`
+ : `/query/v1/stream?query=${questionParam}`
+ )
+ }
+
+ const sendFeedback = async (
+ questionId: string,
+ reaction: AiAssistantFeedbackType
+ ) => {
+ return await sendRequest(
+ `/query/v1/question-answer/${questionId}/feedback`,
+ "POST",
+ {
+ "Content-Type": "application/json",
+ },
+ JSON.stringify({
+ question_id: questionId,
+ reaction,
+ user_identifier: analytics?.user().anonymousId() || "",
+ })
+ )
+ }
+
+ return (
+
+ {children}
+
+
+ console.error(
+ "ReCAPTCHA token not yet configured. Please reach out to the kapa team at founders@kapa.ai to complete the setup."
+ )
+ }
+ className="grecaptcha-badge"
+ />
+
+ )
+}
+
+export const useAiAssistant = () => {
+ const context = useContext(AiAssistantContext)
+
+ if (!context) {
+ throw new Error("useAiAssistant must be used within a AiAssistantProvider")
+ }
+
+ return context
+}
diff --git a/www/packages/docs-ui/src/providers/Search/index.tsx b/www/packages/docs-ui/src/providers/Search/index.tsx
index 3b0b8ac2b0..4ced0f770e 100644
--- a/www/packages/docs-ui/src/providers/Search/index.tsx
+++ b/www/packages/docs-ui/src/providers/Search/index.tsx
@@ -6,10 +6,21 @@ import React, {
useEffect,
useState,
useMemo,
+ useRef,
} from "react"
-import { SearchModal, SearchModalProps } from "@/components"
+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
@@ -17,6 +28,10 @@ export type SearchContextType = {
defaultFilters: string[]
setDefaultFilters: (value: string[]) => void
searchClient: SearchClient
+ commands: SearchCommand[]
+ command: SearchCommand | null
+ setCommand: React.Dispatch>
+ modalRef: React.MutableRefObject
}
const SearchContext = createContext(null)
@@ -32,7 +47,9 @@ export type SearchProviderProps = {
children: React.ReactNode
initialDefaultFilters?: string[]
algolia: AlgoliaProps
- searchProps: Omit
+ searchProps: Omit
+ commands?: SearchCommand[]
+ modalClassName?: string
}
export const SearchProvider = ({
@@ -40,11 +57,16 @@ export const SearchProvider = ({
initialDefaultFilters = [],
searchProps,
algolia,
+ commands = [],
+ modalClassName,
}: SearchProviderProps) => {
const [isOpen, setIsOpen] = useState(false)
const [defaultFilters, setDefaultFilters] = useState(
initialDefaultFilters
)
+ const [command, setCommand] = useState(null)
+
+ const modalRef = useRef(null)
const searchClient: SearchClient = useMemo(() => {
const algoliaClient = algoliasearch(algolia.appId, algolia.apiKey)
@@ -89,10 +111,53 @@ export const SearchProvider = ({
defaultFilters,
setDefaultFilters,
searchClient,
+ commands,
+ command,
+ setCommand,
+ modalRef,
}}
>
{children}
-
+ setIsOpen(false)}
+ passedRef={modalRef}
+ className={modalClassName}
+ >
+
+
+ <>
+ {command === null && (
+
+ )}
+ {command?.component}
+ >
+
+
+
)
}
diff --git a/www/packages/docs-ui/src/providers/index.ts b/www/packages/docs-ui/src/providers/index.ts
index 110e6e6712..32b34fb1e4 100644
--- a/www/packages/docs-ui/src/providers/index.ts
+++ b/www/packages/docs-ui/src/providers/index.ts
@@ -1,3 +1,4 @@
+export * from "./AiAssistant"
export * from "./Analytics"
export * from "./ColorMode"
export * from "./Mobile"
diff --git a/www/packages/tailwind/base.tailwind.config.js b/www/packages/tailwind/base.tailwind.config.js
index d921734414..e2a6531522 100644
--- a/www/packages/tailwind/base.tailwind.config.js
+++ b/www/packages/tailwind/base.tailwind.config.js
@@ -433,7 +433,7 @@ module.exports = {
},
],
},
- keyframes: {
+ keyframes: ({ theme }) => ({
fadeIn: {
from: { opacity: 0 },
to: { opacity: 1 },
@@ -459,6 +459,63 @@ module.exports = {
transform: "scale3d(1, 1, 1)",
},
},
+ fadeInDown: {
+ from: {
+ opacity: "0",
+ transform: "translate3d(0, -100%, 0)",
+ },
+ to: {
+ opacity: "1",
+ transform: "translate3d(0, 0, 0)",
+ },
+ },
+ fadeInLeft: {
+ from: {
+ opacity: "0",
+ transform: "translate3d(-100%, 0, 0)",
+ },
+ to: {
+ opacity: "1",
+ transform: "translate3d(0, 0, 0)",
+ },
+ },
+ fadeInRight: {
+ from: {
+ opacity: "0",
+ transform: "translate3d(100%, 0, 0)",
+ },
+ to: {
+ opacity: "1",
+ transform: "translate3d(0, 0, 0)",
+ },
+ },
+ fadeOutUp: {
+ from: {
+ opacity: "1",
+ },
+ to: {
+ opacity: "0",
+ transform: "translate3d(0, -100%, 0)",
+ },
+ },
+ fadeOutLeft: {
+ from: {
+ opacity: "1",
+ },
+ to: {
+ opacity: "0",
+ transform: "translate3d(-100%, 0, 0)",
+ },
+ },
+ fadeOutRight: {
+ from: {
+ opacity: "1",
+ },
+ to: {
+ opacity: "0",
+ transform: "translate3d(100%, 0, 0)",
+ },
+ },
slideInRight: {
from: {
transform: "translate3d(100%, 0, 0)",
@@ -477,13 +534,48 @@ module.exports = {
transform: "translate3d(100%, 0, 0)",
},
},
- },
+ slideInLeft: {
+ from: {
+ transform: "translate3d(-100%, 0, 0)",
+ visibility: "visible",
+ },
+ to: {
+ transform: "translate3d(0, 0, 0)",
+ },
+ },
+ slideOutLeft: {
+ from: {
+ transform: "translate3d(0, 0, 0)",
+ },
+ to: {
+ visibility: "hidden",
+ transform: "translate3d(-100%, 0, 0)",
+ },
+ },
+ pulsingDots: {
+ "0%": {
+ opacity: 1,
+ },
+ "100%": {
+ opacity: 0.3,
+ },
+ },
+ }),
animation: {
fadeIn: "fadeIn 500ms",
fadeOut: "fadeOut 500ms",
+ fadeInDown: "fadeInDown 500ms",
+ fadeInLeft: "fadeInLeft 500ms",
+ fadeInRight: "fadeInRight 500ms",
+ fadeOutUp: "fadeOutUp 500ms",
+ fadeOutLeft: "fadeOutLeft 500ms",
+ fadeOutRight: "fadeOutRight 500ms",
tada: "tada 1s",
slideInRight: "slideInRight 500ms",
slideOutRight: "slideOutRight 500ms",
+ slideInLeft: "slideInLeft 500ms",
+ slideOutLeft: "slideOutLeft 500ms",
+ pulsingDots: "pulsingDots 1s alternate infinite",
},
},
fontFamily: {
@@ -559,39 +651,65 @@ module.exports = {
"toc-dark": "url('/img/side-menu.svg')",
},
},
+ animationDelay: {
+ 0: "0ms",
+ 200: "200ms",
+ 400: "400ms",
+ },
plugins: [
- plugin(({ addBase, addVariant, addUtilities, addComponents, theme }) => {
- addBase(presets)
- addVariant("search-cancel", "&::-webkit-search-cancel-button")
- addUtilities({
- ".animation-fill-forwards": {
- animationFillMode: "forwards",
- },
- ".animate-fast": {
- animationDuration: "300ms",
- },
- ".clip": {
- clipPath: "inset(0)",
- },
- ".no-marker": {
- "&::-webkit-details-marker": {
- display: "none",
+ plugin(
+ ({
+ addBase,
+ addVariant,
+ addUtilities,
+ addComponents,
+ matchUtilities,
+ theme,
+ }) => {
+ addBase(presets)
+ addVariant("search-cancel", "&::-webkit-search-cancel-button")
+ addUtilities({
+ ".animation-fill-forwards": {
+ animationFillMode: "forwards",
},
- },
- })
- addComponents({
- ".btn-secondary-icon": {
- padding: "4px !important",
- },
- "btn-clear": {
- backgroundColor: "transparent",
- boxShadow: theme("shadow.none"),
- borderWidth: 0,
- borderColor: "transparent",
- outlineColor: "transparent",
- cursor: "pointer",
- },
- })
- }),
+ ".animate-fast": {
+ animationDuration: "300ms",
+ },
+ ".clip": {
+ clipPath: "inset(0)",
+ },
+ ".no-marker": {
+ "&::-webkit-details-marker": {
+ display: "none",
+ },
+ },
+ })
+ addComponents({
+ ".btn-secondary-icon": {
+ padding: "4px !important",
+ },
+ "btn-clear": {
+ backgroundColor: "transparent",
+ boxShadow: theme("shadow.none"),
+ borderWidth: 0,
+ borderColor: "transparent",
+ outlineColor: "transparent",
+ cursor: "pointer",
+ },
+ ".grecaptcha-badge": {
+ visibility: "hidden",
+ },
+ })
+
+ matchUtilities(
+ {
+ "animation-delay": (value) => ({
+ animationDelay: value,
+ }),
+ },
+ { values: theme("animationDelay") }
+ )
+ }
+ ),
],
}
diff --git a/www/yarn.lock b/www/yarn.lock
index 1518766083..d7ba91979b 100644
--- a/www/yarn.lock
+++ b/www/yarn.lock
@@ -3138,6 +3138,13 @@ __metadata:
languageName: node
linkType: hard
+"@juggle/resize-observer@npm:^3.3.1":
+ version: 3.4.0
+ resolution: "@juggle/resize-observer@npm:3.4.0"
+ checksum: 12930242357298c6f2ad5d4ec7cf631dfb344ca7c8c830ab7f64e6ac11eb1aae486901d8d880fd08fb1b257800c160a0da3aee1e7ed9adac0ccbb9b7c5d93347
+ languageName: node
+ linkType: hard
+
"@leichtgewicht/ip-codec@npm:^2.0.1":
version: 2.0.4
resolution: "@leichtgewicht/ip-codec@npm:2.0.4"
@@ -5102,6 +5109,37 @@ __metadata:
languageName: node
linkType: hard
+"@react-hook/latest@npm:^1.0.2":
+ version: 1.0.3
+ resolution: "@react-hook/latest@npm:1.0.3"
+ peerDependencies:
+ react: ">=16.8"
+ checksum: d6a166c21121da519a516e8089ba28a2779d37b6017732ab55476c0d354754ad215394135765254f8752a7c6661c3fb868d088769a644848602f00f8821248ed
+ languageName: node
+ linkType: hard
+
+"@react-hook/passive-layout-effect@npm:^1.2.0":
+ version: 1.2.1
+ resolution: "@react-hook/passive-layout-effect@npm:1.2.1"
+ peerDependencies:
+ react: ">=16.8"
+ checksum: 5c9e6b3df1c91fc2b1d4f711ca96b5f8cb3f6a13a2e97dac7cce623e58d7ee57999c45db3778d0af0b2522b3a5b7463232ef21cb3ee9900437172d48f766d933
+ languageName: node
+ linkType: hard
+
+"@react-hook/resize-observer@npm:^1.2.6":
+ version: 1.2.6
+ resolution: "@react-hook/resize-observer@npm:1.2.6"
+ dependencies:
+ "@juggle/resize-observer": ^3.3.1
+ "@react-hook/latest": ^1.0.2
+ "@react-hook/passive-layout-effect": ^1.2.0
+ peerDependencies:
+ react: ">=16.8"
+ checksum: 6ebe4ded4dc4602906c4c727871f93ea73754dd5758f90d50e5fc7382b1844324a46a4c2e0842d8ad5bf95886091ba8a0c9d3a1ef0f10bd0c9e011ecd7aeea42
+ languageName: node
+ linkType: hard
+
"@react-stately/datepicker@npm:^3.5.0, @react-stately/datepicker@npm:^3.7.0":
version: 3.7.0
resolution: "@react-stately/datepicker@npm:3.7.0"
@@ -7252,6 +7290,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/prop-types@npm:^15.0.0":
+ version: 15.7.7
+ resolution: "@types/prop-types@npm:15.7.7"
+ checksum: 26d565ebae8c28dede71547d687367ce74eeccc645fdbef2d38478fe293996be24784fa6190586ba303ccd274aa94d8a631d36a5d9b8e0c08f5647ff3244d72c
+ languageName: node
+ linkType: hard
+
"@types/qs@npm:*, @types/qs@npm:^6.5.3":
version: 6.9.8
resolution: "@types/qs@npm:6.9.8"
@@ -7284,6 +7329,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/react-google-recaptcha@npm:^2.1.6":
+ version: 2.1.6
+ resolution: "@types/react-google-recaptcha@npm:2.1.6"
+ dependencies:
+ "@types/react": "*"
+ checksum: 07b0d14aaf9fb89063a2a91b80f088ab5eb99dfd76e6f24ad2deddf44eee53d620ebb93e9e5bcd0f4b1ff320c80b7bc1c5e2c7107f4fda01538323aeecf4c7b5
+ languageName: node
+ linkType: hard
+
"@types/react-router-config@npm:*, @types/react-router-config@npm:^5.0.6":
version: 5.0.7
resolution: "@types/react-router-config@npm:5.0.7"
@@ -10181,9 +10235,11 @@ __metadata:
"@medusajs/icons": latest
"@medusajs/ui": latest
"@octokit/request": ^8.1.1
+ "@react-hook/resize-observer": ^1.2.6
"@segment/analytics-next": ^1.55.0
"@types/react": ^17.0.1
"@types/react-dom": ^17.0.1
+ "@types/react-google-recaptcha": ^2.1.6
algoliasearch: ^4.20.0
clsx: ^2.0.0
cpy-cli: ^5.0.0
@@ -10192,7 +10248,9 @@ __metadata:
prism-react-renderer: ^2.0.6
react: ^17.0.1
react-dom: ^17.0.1
+ react-google-recaptcha: ^3.1.0
react-instantsearch: ^7.0.3
+ react-markdown: ^8.0.7
react-tooltip: ^5.21.3
react-transition-group: ^4.4.5
react-uuid: ^2.0.0
@@ -12702,7 +12760,7 @@ __metadata:
languageName: node
linkType: hard
-"hoist-non-react-statics@npm:^3.1.0":
+"hoist-non-react-statics@npm:^3.1.0, hoist-non-react-statics@npm:^3.3.0":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies:
@@ -17092,7 +17150,7 @@ __metadata:
languageName: node
linkType: hard
-"prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
+"prop-types@npm:^15.0.0, prop-types@npm:^15.5.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -17291,6 +17349,18 @@ __metadata:
languageName: node
linkType: hard
+"react-async-script@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "react-async-script@npm:1.2.0"
+ dependencies:
+ hoist-non-react-statics: ^3.3.0
+ prop-types: ^15.5.0
+ peerDependencies:
+ react: ">=16.4.1"
+ checksum: 89450912110c380abc08258ce17d2fb18d31d6b7179a74f6bc504c0761a4ca271edb671e402fa8e5ea4250b5c17fa953af80a9f1c4ebb26c9e81caee8476c903
+ languageName: node
+ linkType: hard
+
"react-base16-styling@npm:^0.6.0":
version: 0.6.0
resolution: "react-base16-styling@npm:0.6.0"
@@ -17393,6 +17463,18 @@ __metadata:
languageName: node
linkType: hard
+"react-google-recaptcha@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "react-google-recaptcha@npm:3.1.0"
+ dependencies:
+ prop-types: ^15.5.0
+ react-async-script: ^1.2.0
+ peerDependencies:
+ react: ">=16.4.1"
+ checksum: 5ecaa6b88f238defd939012cb2671b4cbda59fe03f059158994b8c5215db482412e905eae6a67d23cef220d77cfe0430ab91ce7849d476515cda72f3a8a0a746
+ languageName: node
+ linkType: hard
+
"react-helmet-async@npm:*, react-helmet-async@npm:^1.3.0":
version: 1.3.0
resolution: "react-helmet-async@npm:1.3.0"
@@ -17455,6 +17537,13 @@ __metadata:
languageName: node
linkType: hard
+"react-is@npm:^18.0.0":
+ version: 18.2.0
+ resolution: "react-is@npm:18.2.0"
+ checksum: 6eb5e4b28028c23e2bfcf73371e72cd4162e4ac7ab445ddae2afe24e347a37d6dc22fae6e1748632cd43c6d4f9b8f86dcf26bf9275e1874f436d129952528ae0
+ languageName: node
+ linkType: hard
+
"react-json-view@npm:^1.21.3":
version: 1.21.3
resolution: "react-json-view@npm:1.21.3"
@@ -17489,6 +17578,32 @@ __metadata:
languageName: node
linkType: hard
+"react-markdown@npm:^8.0.7":
+ version: 8.0.7
+ resolution: "react-markdown@npm:8.0.7"
+ dependencies:
+ "@types/hast": ^2.0.0
+ "@types/prop-types": ^15.0.0
+ "@types/unist": ^2.0.0
+ comma-separated-tokens: ^2.0.0
+ hast-util-whitespace: ^2.0.0
+ prop-types: ^15.0.0
+ property-information: ^6.0.0
+ react-is: ^18.0.0
+ remark-parse: ^10.0.0
+ remark-rehype: ^10.0.0
+ space-separated-tokens: ^2.0.0
+ style-to-object: ^0.4.0
+ unified: ^10.0.0
+ unist-util-visit: ^4.0.0
+ vfile: ^5.0.0
+ peerDependencies:
+ "@types/react": ">=16"
+ react: ">=16"
+ checksum: 016617fbd2f4c03c5ae017fe39e89202f2ff536b4921dc1a5f7283d4b9d5157f20797adda75a8c59a06787ad0bc8841e2e437915aec645ce528e0a04a6d450ac
+ languageName: node
+ linkType: hard
+
"react-remove-scroll-bar@npm:^2.3.3":
version: 2.3.4
resolution: "react-remove-scroll-bar@npm:2.3.4"
@@ -19072,7 +19187,7 @@ __metadata:
languageName: node
linkType: hard
-"style-to-object@npm:^0.4.1":
+"style-to-object@npm:^0.4.0, style-to-object@npm:^0.4.1":
version: 0.4.2
resolution: "style-to-object@npm:0.4.2"
dependencies: