docs: add search to workflows reference (#11054)
* docs: add search to workflows reference * fix error
This commit is contained in:
1 parent
5b9cb5a2be
commit
af350c3a8b
15 files changed
+349
-112
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { BookIcon, Card, IconHeadline, WindowPaintbrushIcon } from "docs-ui"
|
import { Card, IconHeadline, WindowPaintbrushIcon } from "docs-ui"
|
||||||
|
import { Book } from "@medusajs/icons"
|
||||||
import { basePathUrl } from "../../../utils/base-path-url"
|
import { basePathUrl } from "../../../utils/base-path-url"
|
||||||
import HomepageCodeTabs from "../CodeTabs"
|
import HomepageCodeTabs from "../CodeTabs"
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ const HomepageTopSection = () => {
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col gap-1.5 pt-1 xs:pt-4 lg:py-4">
|
<div className="flex flex-col gap-1.5 pt-1 xs:pt-4 lg:py-4">
|
||||||
<div className="flex flex-col gap-[10px]">
|
<div className="flex flex-col gap-[10px]">
|
||||||
<IconHeadline title="Documentation" icon={<BookIcon />} />
|
<IconHeadline title="Documentation" icon={<Book />} />
|
||||||
<h2 className="text-medusa-fg-base text-h1 text-pretty w-full md:w-2/3 lg:w-full">
|
<h2 className="text-medusa-fg-base text-h1 text-pretty w-full md:w-2/3 lg:w-full">
|
||||||
Learn how to build Medusa projects. Explore our guides.
|
Learn how to build Medusa projects. Explore our guides.
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -20,4 +20,8 @@ Through this reference, you'll learn about the available workflows and steps in
|
|||||||
|
|
||||||
All workflows and steps in this reference are exported by the `@medusajs/medusa/core-flows` package.
|
All workflows and steps in this reference are exported by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
<ChildDocs childLevel={2} hideItems={["Overview", "Steps"]} defaultItemsPerRow={2} />
|
<ChildDocs childLevel={2} hideItems={["Overview", "Steps"]} defaultItemsPerRow={2} search={{
|
||||||
|
enable: true,
|
||||||
|
placeholder: "Search workflows...",
|
||||||
|
storageKey: "medusa-workflows-reference",
|
||||||
|
}} />
|
||||||
@@ -67,6 +67,7 @@
|
|||||||
"algoliasearch": "^5.2.1",
|
"algoliasearch": "^5.2.1",
|
||||||
"framer-motion": "^11.11.9",
|
"framer-motion": "^11.11.9",
|
||||||
"mermaid": "^10.9.0",
|
"mermaid": "^10.9.0",
|
||||||
|
"minisearch": "^7.1.1",
|
||||||
"npm-to-yarn": "^2.1.0",
|
"npm-to-yarn": "^2.1.0",
|
||||||
"prism-react-renderer": "2.4.0",
|
"prism-react-renderer": "2.4.0",
|
||||||
"react": "rc",
|
"react": "rc",
|
||||||
|
|||||||
@@ -17,9 +17,35 @@ export const CardDefaultLayout = ({
|
|||||||
children,
|
children,
|
||||||
badge,
|
badge,
|
||||||
rightIcon: RightIconComponent,
|
rightIcon: RightIconComponent,
|
||||||
|
highlightText = [],
|
||||||
}: CardProps) => {
|
}: CardProps) => {
|
||||||
const isExternal = useIsExternalLink({ href })
|
const isExternal = useIsExternalLink({ href })
|
||||||
|
|
||||||
|
const getHighlightedText = (textToHighlight: string) => {
|
||||||
|
if (!highlightText.length) {
|
||||||
|
return textToHighlight
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = textToHighlight.split(
|
||||||
|
new RegExp(`(${highlightText.join("|")})`, "gi")
|
||||||
|
)
|
||||||
|
return parts.map((part, index) => {
|
||||||
|
const isHighlighted = highlightText.some((highlight) => {
|
||||||
|
return part.toLowerCase() === highlight.toLowerCase()
|
||||||
|
})
|
||||||
|
return isHighlighted ? (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="bg-medusa-tag-blue-bg px-px rounded-s-docs_xxs"
|
||||||
|
>
|
||||||
|
{part}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
part
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -52,11 +78,13 @@ export const CardDefaultLayout = ({
|
|||||||
>
|
>
|
||||||
{title && (
|
{title && (
|
||||||
<div className="text-small-plus text-medusa-fg-base truncate">
|
<div className="text-small-plus text-medusa-fg-base truncate">
|
||||||
{title}
|
{getHighlightedText(title)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{text && (
|
{text && (
|
||||||
<span className="text-small-plus text-medusa-fg-subtle">{text}</span>
|
<span className="text-small-plus text-medusa-fg-subtle">
|
||||||
|
{getHighlightedText(text)}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type CardProps = {
|
|||||||
iconClassName?: string
|
iconClassName?: string
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
badge?: BadgeProps
|
badge?: BadgeProps
|
||||||
|
highlightText?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Card = ({ type = "default", ...props }: CardProps) => {
|
export const Card = ({ type = "default", ...props }: CardProps) => {
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { IconProps } from "@medusajs/icons/dist/types"
|
|
||||||
|
|
||||||
export const BookIcon = (props: IconProps) => {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<g clipPath="url(#clip0_10569_98393)">
|
|
||||||
<path
|
|
||||||
opacity="0.3"
|
|
||||||
d="M13.5556 1.55566H5.11111V11.3334H13.5556V1.55566Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M5.11111 1.55566V11.3334"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M2.44444 12.889V3.33344C2.44444 2.35122 3.24 1.55566 4.22222 1.55566H13.5556V11.3334"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M4.66666 14.4445H4C3.14133 14.4445 2.44444 13.7485 2.44444 12.8889C2.44444 12.0294 3.14133 11.3334 4 11.3334H13.5556C12.9858 12.0836 12.9031 13.5974 13.5556 14.4445H4.66666Z"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M7.77777 4.66675H10.8889"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M7.77777 7.33337H10.8889"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0_10569_98393">
|
|
||||||
<rect width="16" height="16" fill="white" />
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from "./AiAssistant"
|
export * from "./AiAssistant"
|
||||||
export * from "./Book"
|
|
||||||
export * from "./CalendarRefresh"
|
export * from "./CalendarRefresh"
|
||||||
export * from "./ChefHat"
|
export * from "./ChefHat"
|
||||||
export * from "./CircleDottedLine"
|
export * from "./CircleDottedLine"
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { MagnifyingGlass, XMark } from "@medusajs/icons"
|
||||||
|
import clsx from "clsx"
|
||||||
|
import React from "react"
|
||||||
|
import { useKeyboardShortcut } from "../../../hooks"
|
||||||
|
import { Kbd } from "../../Kbd"
|
||||||
|
|
||||||
|
type SearchInputProps = {
|
||||||
|
onChange: (value: string) => void
|
||||||
|
} & Omit<React.ComponentProps<"input">, "onChange">
|
||||||
|
|
||||||
|
export const SearchInput = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
className,
|
||||||
|
placeholder = "Search...",
|
||||||
|
...props
|
||||||
|
}: SearchInputProps) => {
|
||||||
|
useKeyboardShortcut({
|
||||||
|
metakey: false,
|
||||||
|
shortcutKeys: ["escape"],
|
||||||
|
action: () => onChange(""),
|
||||||
|
checkEditing: false,
|
||||||
|
preventDefault: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-docs_0.5">
|
||||||
|
<div className="relative">
|
||||||
|
<MagnifyingGlass className="absolute left-docs_0.5 top-[8.5px] bottom-[8.5px] text-medusa-fg-muted" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={clsx(
|
||||||
|
"w-full h-docs_2 pl-docs_2 text-compact-small placeholder:text-medusa-fg-muted",
|
||||||
|
"bg-medusa-bg-field text-medusa-fg-base rounded-full",
|
||||||
|
"shadow-borders-base hover:bg-medusa-bg-field-hover",
|
||||||
|
"focus:bg-medusa-bg-field focus:shadow-borders-interactive-with-active focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
{value && (
|
||||||
|
<button
|
||||||
|
className={clsx(
|
||||||
|
"absolute right-docs_0.5 top-[8.5px] bottom-[8.5px] appearance-none",
|
||||||
|
"flex items-center justify-center"
|
||||||
|
)}
|
||||||
|
onClick={() => onChange("")}
|
||||||
|
>
|
||||||
|
<XMark className="text-medusa-fg-muted" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="flex gap-docs_0.25 justify-end items-center text-compact-x-small">
|
||||||
|
<Kbd variant="small">esc</Kbd>
|
||||||
|
<span className="text-medusa-fg-muted">Clear Search</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
|
||||||
export type KbdProps = React.ComponentProps<"kbd">
|
export type KbdProps = React.ComponentProps<"kbd"> & {
|
||||||
|
variant?: "default" | "small"
|
||||||
|
}
|
||||||
|
|
||||||
export const Kbd = ({ children, className, ...props }: KbdProps) => {
|
export const Kbd = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
variant = "default",
|
||||||
|
...props
|
||||||
|
}: KbdProps) => {
|
||||||
return (
|
return (
|
||||||
<kbd
|
<kbd
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@@ -12,7 +19,10 @@ export const Kbd = ({ children, className, ...props }: KbdProps) => {
|
|||||||
"py-0 px-docs_0.25",
|
"py-0 px-docs_0.25",
|
||||||
"bg-medusa-bg-field",
|
"bg-medusa-bg-field",
|
||||||
"text-medusa-fg-subtle",
|
"text-medusa-fg-subtle",
|
||||||
"text-compact-x-small-plus font-base shadow-none",
|
"font-base shadow-none",
|
||||||
|
variant === "small"
|
||||||
|
? "text-compact-x-small"
|
||||||
|
: "text-compact-x-small-plus",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
BarsThree,
|
BarsThree,
|
||||||
|
Book,
|
||||||
QuestionMarkCircle,
|
QuestionMarkCircle,
|
||||||
SidebarLeft,
|
SidebarLeft,
|
||||||
TimelineVertical,
|
TimelineVertical,
|
||||||
} from "@medusajs/icons"
|
} from "@medusajs/icons"
|
||||||
import React, { useMemo, useRef, useState } from "react"
|
import React, { useMemo, useRef, useState } from "react"
|
||||||
import {
|
import {
|
||||||
BookIcon,
|
|
||||||
Button,
|
Button,
|
||||||
getOsShortcut,
|
getOsShortcut,
|
||||||
Menu,
|
Menu,
|
||||||
@@ -41,7 +41,7 @@ export const MainNavDesktopMenu = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
icon: <BookIcon />,
|
icon: <Book />,
|
||||||
title: "Medusa v1",
|
title: "Medusa v1",
|
||||||
link: "https://docs.medusajs.com/v1",
|
link: "https://docs.medusajs.com/v1",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export * from "./InlineIcon"
|
|||||||
export * from "./InlineThemeImage"
|
export * from "./InlineThemeImage"
|
||||||
export * from "./InlineCode"
|
export * from "./InlineCode"
|
||||||
export * from "./Input/Text"
|
export * from "./Input/Text"
|
||||||
|
export * from "./Input/Search"
|
||||||
export * from "./Kbd"
|
export * from "./Kbd"
|
||||||
export * from "./Label"
|
export * from "./Label"
|
||||||
export * from "./LearningPath"
|
export * from "./LearningPath"
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { useCallback, useMemo } from "react"
|
import React, { useCallback, useEffect, useMemo, useState } from "react"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardList,
|
CardList,
|
||||||
|
getLocalSearch,
|
||||||
H2,
|
H2,
|
||||||
H3,
|
H3,
|
||||||
H4,
|
H4,
|
||||||
Hr,
|
Hr,
|
||||||
isSidebarItemLink,
|
isSidebarItemLink,
|
||||||
|
LocalSearch,
|
||||||
MarkdownContent,
|
MarkdownContent,
|
||||||
|
SearchInput,
|
||||||
|
useIsBrowser,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from "../.."
|
} from "../.."
|
||||||
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
|
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
|
||||||
import slugify from "slugify"
|
import slugify from "slugify"
|
||||||
import { MDXComponents } from "../.."
|
import { MDXComponents } from "../.."
|
||||||
import { ChevronDoubleRight } from "@medusajs/icons"
|
import { ChevronDoubleRight, ExclamationCircle } from "@medusajs/icons"
|
||||||
|
|
||||||
type HeadingComponent = (
|
type HeadingComponent = (
|
||||||
props: React.HTMLAttributes<HTMLHeadingElement>
|
props: React.HTMLAttributes<HTMLHeadingElement>
|
||||||
@@ -32,6 +36,11 @@ export type UseChildDocsProps = {
|
|||||||
childLevel?: number
|
childLevel?: number
|
||||||
itemsPerRow?: number
|
itemsPerRow?: number
|
||||||
defaultItemsPerRow?: number
|
defaultItemsPerRow?: number
|
||||||
|
search?: {
|
||||||
|
enable: boolean
|
||||||
|
storageKey?: string
|
||||||
|
placeholder?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useChildDocs = ({
|
export const useChildDocs = ({
|
||||||
@@ -45,8 +54,18 @@ export const useChildDocs = ({
|
|||||||
childLevel = 1,
|
childLevel = 1,
|
||||||
itemsPerRow,
|
itemsPerRow,
|
||||||
defaultItemsPerRow,
|
defaultItemsPerRow,
|
||||||
|
search: {
|
||||||
|
enable: enableSearch = false,
|
||||||
|
storageKey = "child-docs",
|
||||||
|
...searchProps
|
||||||
|
} = { enable: false },
|
||||||
}: UseChildDocsProps) => {
|
}: UseChildDocsProps) => {
|
||||||
const { currentItems, activeItem } = useSidebar()
|
const { currentItems, activeItem } = useSidebar()
|
||||||
|
const { isBrowser } = useIsBrowser()
|
||||||
|
const [searchQuery, setSearchQuery] = useState("")
|
||||||
|
const [localSearch, setLocalSearch] = useState<
|
||||||
|
LocalSearch<SidebarItemLink> | undefined
|
||||||
|
>()
|
||||||
const TitleHeaderComponent = useCallback(
|
const TitleHeaderComponent = useCallback(
|
||||||
(level: number): HeadingComponent => {
|
(level: number): HeadingComponent => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
@@ -92,16 +111,11 @@ export const useChildDocs = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterItems = (items: SidebarItem[]): SidebarItem[] => {
|
const filterItems = (items: SidebarItem[]): InteractiveSidebarItem[] => {
|
||||||
return items
|
return (items.filter(filterCondition) as InteractiveSidebarItem[])
|
||||||
.filter(filterCondition)
|
|
||||||
.map((item) => Object.assign({}, item))
|
.map((item) => Object.assign({}, item))
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
if (
|
if (item.children && filterType === "hide") {
|
||||||
item.type !== "separator" &&
|
|
||||||
item.children &&
|
|
||||||
filterType === "hide"
|
|
||||||
) {
|
|
||||||
item.children = filterItems(item.children)
|
item.children = filterItems(item.children)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,25 +123,6 @@ export const useChildDocs = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredItems = useMemo(() => {
|
|
||||||
const targetItems =
|
|
||||||
type === "sidebar"
|
|
||||||
? currentItems
|
|
||||||
? Object.assign({}, currentItems)
|
|
||||||
: undefined
|
|
||||||
: {
|
|
||||||
default: [...(activeItem?.children || [])],
|
|
||||||
}
|
|
||||||
if (filterType === "all" || !targetItems) {
|
|
||||||
return targetItems
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...targetItems,
|
|
||||||
default: filterItems(targetItems.default),
|
|
||||||
}
|
|
||||||
}, [currentItems, type, activeItem, filterItems])
|
|
||||||
|
|
||||||
const filterNonInteractiveItems = (
|
const filterNonInteractiveItems = (
|
||||||
items: SidebarItem[] | undefined
|
items: SidebarItem[] | undefined
|
||||||
): InteractiveSidebarItem[] => {
|
): InteractiveSidebarItem[] => {
|
||||||
@@ -164,11 +159,115 @@ export const useChildDocs = ({
|
|||||||
return childrenResult
|
return childrenResult
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTopLevelElms = (items?: SidebarItem[]) => {
|
const filteredItems = useMemo(() => {
|
||||||
|
let targetItems =
|
||||||
|
type === "sidebar"
|
||||||
|
? currentItems
|
||||||
|
? Object.assign({}, currentItems)
|
||||||
|
: undefined
|
||||||
|
: {
|
||||||
|
default: [...(activeItem?.children || [])],
|
||||||
|
}
|
||||||
|
if (filterType !== "all" && targetItems) {
|
||||||
|
targetItems = {
|
||||||
|
...targetItems,
|
||||||
|
default: filterItems(targetItems.default),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetItems
|
||||||
|
? {
|
||||||
|
...targetItems,
|
||||||
|
default: filterNonInteractiveItems(targetItems?.default),
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}, [currentItems, type, activeItem, filterType])
|
||||||
|
|
||||||
|
const searchableItems = useMemo(() => {
|
||||||
|
const searchableItems: SidebarItemLink[] = []
|
||||||
|
if (!enableSearch) {
|
||||||
|
return searchableItems
|
||||||
|
}
|
||||||
|
if (onlyTopLevel) {
|
||||||
|
filteredItems?.default?.forEach((item) => {
|
||||||
|
if (isSidebarItemLink(item)) {
|
||||||
|
searchableItems.push(item)
|
||||||
|
} else {
|
||||||
|
const firstChild = item.children?.find((child) =>
|
||||||
|
isSidebarItemLink(child)
|
||||||
|
)
|
||||||
|
if (firstChild) {
|
||||||
|
searchableItems.push(firstChild as SidebarItemLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
filteredItems?.default?.forEach((item) => {
|
||||||
|
const childItems: SidebarItemLink[] =
|
||||||
|
(getChildrenForLevel(item)?.filter((childItem) => {
|
||||||
|
return isSidebarItemLink(childItem)
|
||||||
|
}) as SidebarItemLink[]) || []
|
||||||
|
searchableItems.push(...childItems)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return searchableItems
|
||||||
|
}, [filteredItems, onlyTopLevel, enableSearch])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enableSearch && localSearch) {
|
||||||
|
setLocalSearch(undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!enableSearch || !searchableItems?.length || localSearch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocalSearch(
|
||||||
|
getLocalSearch<SidebarItemLink>({
|
||||||
|
docs: searchableItems,
|
||||||
|
searchableFields: ["title", "description"],
|
||||||
|
options: {
|
||||||
|
storeFields: ["title", "description", "path", "type"],
|
||||||
|
searchOptions: {
|
||||||
|
boost: { title: 2 },
|
||||||
|
prefix: true,
|
||||||
|
fuzzy: 0.2,
|
||||||
|
},
|
||||||
|
idField: "path",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [searchableItems, enableSearch, localSearch])
|
||||||
|
|
||||||
|
const searchResult = useMemo(() => {
|
||||||
|
return localSearch?.search(searchQuery) || []
|
||||||
|
}, [localSearch, searchQuery])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isBrowser || !enableSearch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const storedQuery = localStorage.getItem(`${storageKey}-query`)
|
||||||
|
if (storedQuery) {
|
||||||
|
setSearchQuery(storedQuery)
|
||||||
|
}
|
||||||
|
}, [isBrowser, storageKey, enableSearch])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isBrowser || !enableSearch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(`${storageKey}-query`, searchQuery)
|
||||||
|
}, [isBrowser, searchQuery, storageKey, enableSearch])
|
||||||
|
|
||||||
|
const getTopLevelElms = (items?: InteractiveSidebarItem[]) => {
|
||||||
return (
|
return (
|
||||||
<CardList
|
<CardList
|
||||||
items={
|
items={
|
||||||
filterNonInteractiveItems(items).map((childItem) => {
|
items?.map((childItem) => {
|
||||||
const href = isSidebarItemLink(childItem)
|
const href = isSidebarItemLink(childItem)
|
||||||
? childItem.path
|
? childItem.path
|
||||||
: childItem.children?.length
|
: childItem.children?.length
|
||||||
@@ -193,11 +292,10 @@ export const useChildDocs = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAllLevelsElms = (
|
const getAllLevelsElms = (
|
||||||
items?: SidebarItem[],
|
items?: InteractiveSidebarItem[],
|
||||||
headerLevel = titleLevel
|
headerLevel = titleLevel
|
||||||
) => {
|
) => {
|
||||||
const filteredItems = filterNonInteractiveItems(items)
|
return items?.map((item, key) => {
|
||||||
return filteredItems.map((item, key) => {
|
|
||||||
const itemChildren = getChildrenForLevel(item)
|
const itemChildren = getChildrenForLevel(item)
|
||||||
const HeadingComponent = itemChildren?.length
|
const HeadingComponent = itemChildren?.length
|
||||||
? TitleHeaderComponent(headerLevel)
|
? TitleHeaderComponent(headerLevel)
|
||||||
@@ -243,7 +341,7 @@ export const useChildDocs = ({
|
|||||||
defaultItemsPerRow={defaultItemsPerRow}
|
defaultItemsPerRow={defaultItemsPerRow}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{key !== filteredItems.length - 1 && headerLevel === 2 && <Hr />}
|
{key !== items.length - 1 && headerLevel === 2 && <Hr />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!HeadingComponent && isSidebarItemLink(item) && (
|
{!HeadingComponent && isSidebarItemLink(item) && (
|
||||||
@@ -259,12 +357,64 @@ export const useChildDocs = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getElms = (items?: SidebarItem[]) => {
|
const getSearchResultElms = () => {
|
||||||
return onlyTopLevel ? getTopLevelElms(items) : getAllLevelsElms(items)
|
const Heading = TitleHeaderComponent(titleLevel)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Heading>Search Results</Heading>
|
||||||
|
{searchResult.length > 0 && (
|
||||||
|
<CardList
|
||||||
|
items={searchResult.map((item) => ({
|
||||||
|
title: item.title,
|
||||||
|
href: item.path,
|
||||||
|
text: item.description,
|
||||||
|
rightIcon: item.type === "ref" ? ChevronDoubleRight : undefined,
|
||||||
|
highlightText: item.terms,
|
||||||
|
}))}
|
||||||
|
itemsPerRow={itemsPerRow}
|
||||||
|
defaultItemsPerRow={defaultItemsPerRow}
|
||||||
|
className="my-docs_2"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!searchResult.length && (
|
||||||
|
<div className="flex flex-col justify-center items-center gap-docs_0.75">
|
||||||
|
<ExclamationCircle className="text-medusa-fg-subtle" />
|
||||||
|
<span className="text-compact-small-plus text-medusa-fg-subtle text-center">
|
||||||
|
No results found matching your query.
|
||||||
|
</span>
|
||||||
|
<span className="text-compact-small text-medusa-fg-muted text-center">
|
||||||
|
Try searching with another term or clearing the search.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getElms = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{enableSearch && (
|
||||||
|
<SearchInput
|
||||||
|
value={searchQuery || ""}
|
||||||
|
onChange={setSearchQuery}
|
||||||
|
{...searchProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{searchQuery && getSearchResultElms()}
|
||||||
|
{!searchQuery && (
|
||||||
|
<>
|
||||||
|
{onlyTopLevel
|
||||||
|
? getTopLevelElms(filteredItems?.default)
|
||||||
|
: getAllLevelsElms(filteredItems?.default)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: filteredItems,
|
items: filteredItems,
|
||||||
component: getElms(filteredItems?.default),
|
component: getElms(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import MiniSearch, { Options as MiniSearchOptions } from "minisearch"
|
||||||
|
|
||||||
|
type BaseSearchRecord = Record<string, unknown>
|
||||||
|
|
||||||
|
type GetLocalSearchInput<T extends BaseSearchRecord = BaseSearchRecord> = {
|
||||||
|
docs: T[]
|
||||||
|
searchableFields: string[]
|
||||||
|
options?: Omit<MiniSearchOptions, "fields">
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchResult<T> = (T & {
|
||||||
|
terms?: string[]
|
||||||
|
})[]
|
||||||
|
|
||||||
|
export type LocalSearch<T extends BaseSearchRecord = BaseSearchRecord> =
|
||||||
|
MiniSearch & {
|
||||||
|
search: (query: string) => SearchResult<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLocalSearch = <T extends BaseSearchRecord = BaseSearchRecord>({
|
||||||
|
docs,
|
||||||
|
searchableFields,
|
||||||
|
options,
|
||||||
|
}: GetLocalSearchInput<T>): LocalSearch<T> => {
|
||||||
|
const miniSearch = new MiniSearch({
|
||||||
|
fields: searchableFields,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
miniSearch.addAll(docs)
|
||||||
|
|
||||||
|
return miniSearch as LocalSearch<T>
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ export * from "./check-sidebar-item-visibility"
|
|||||||
export * from "./decode-str"
|
export * from "./decode-str"
|
||||||
export * from "./dom-utils"
|
export * from "./dom-utils"
|
||||||
export * from "./get-link-with-base-path"
|
export * from "./get-link-with-base-path"
|
||||||
|
export * from "./get-local-search"
|
||||||
export * from "./get-navbar-items"
|
export * from "./get-navbar-items"
|
||||||
export * from "./os-browser-utils"
|
export * from "./os-browser-utils"
|
||||||
export * from "./get-scrolled-top"
|
export * from "./get-scrolled-top"
|
||||||
|
|||||||
@@ -8445,6 +8445,7 @@ __metadata:
|
|||||||
eslint: ^9.13.0
|
eslint: ^9.13.0
|
||||||
framer-motion: ^11.11.9
|
framer-motion: ^11.11.9
|
||||||
mermaid: ^10.9.0
|
mermaid: ^10.9.0
|
||||||
|
minisearch: ^7.1.1
|
||||||
next: 15.0.4
|
next: 15.0.4
|
||||||
npm-to-yarn: ^2.1.0
|
npm-to-yarn: ^2.1.0
|
||||||
prism-react-renderer: 2.4.0
|
prism-react-renderer: 2.4.0
|
||||||
@@ -13011,6 +13012,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"minisearch@npm:^7.1.1":
|
||||||
|
version: 7.1.1
|
||||||
|
resolution: "minisearch@npm:7.1.1"
|
||||||
|
checksum: a601963ae5fa3b2e884278c92f614187651f2734e248cb564236258cb307cbe6aab2f985962f77939a6255da123d625e38ff6d72fa9c4164ac3e49477fbad9f5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
|
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
|
||||||
version: 2.1.2
|
version: 2.1.2
|
||||||
resolution: "minizlib@npm:2.1.2"
|
resolution: "minizlib@npm:2.1.2"
|
||||||
|
|||||||
Reference in new issue
Block a user