docs: redesign table of content (#12647)
* implement toc * added to projects * fixes and adapt for references * added product frontmatter * remove action menu from 404 pages
This commit is contained in:
@@ -142,10 +142,10 @@ export const AiAssistantChatWindow = () => {
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
"flex z-50 w-[calc(100%-8px)] md:w-ai-assistant transition-[height,right]",
|
||||
"flex z-50 w-[calc(100%-8px)] md:w-ai-assistant transition-[right]",
|
||||
"absolute -right-[150%] sm:-right-full top-0",
|
||||
type === "default" && [
|
||||
"xxl:w-0 xxl:relative xxl:transition-[height,right,width]",
|
||||
"xxl:w-0 xxl:relative xxl:transition-[right,width]",
|
||||
"xxl:shadow-elevation-card-rest xxl:dark:shadow-elevation-card-rest-dark",
|
||||
chatOpened && "xxl:!w-ai-assistant",
|
||||
],
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
import React from "react"
|
||||
import clsx from "clsx"
|
||||
import { CardProps } from "../.."
|
||||
import { BorderedIcon, ThemeImage, useIsExternalLink } from "../../../.."
|
||||
import {
|
||||
BorderedIcon,
|
||||
Button,
|
||||
ThemeImage,
|
||||
useIsExternalLink,
|
||||
} from "../../../.."
|
||||
import Link from "next/link"
|
||||
import Image from "next/image"
|
||||
import { ArrowUpRightOnBox, TriangleRightMini } from "@medusajs/icons"
|
||||
import { ArrowUpRightOnBox, TriangleRightMini, XMark } from "@medusajs/icons"
|
||||
|
||||
export const CardLayoutMini = ({
|
||||
icon,
|
||||
@@ -15,6 +20,11 @@ export const CardLayoutMini = ({
|
||||
title,
|
||||
text,
|
||||
href,
|
||||
hrefProps = {},
|
||||
closeable = false,
|
||||
onClose,
|
||||
className,
|
||||
imageDimensions = { width: 45, height: 36 },
|
||||
}: CardProps) => {
|
||||
const isExternal = useIsExternalLink({ href })
|
||||
|
||||
@@ -26,7 +36,8 @@ export const CardLayoutMini = ({
|
||||
"hover:shadow-elevation-card-hover dark:hover:shadow-elevation-card-hover-dark",
|
||||
"bg-medusa-tag-neutral-bg dark:bg-medusa-bg-component",
|
||||
"hover:bg-medusa-tag-neutral-bg-hover dark:hover:bg-medusa-bg-component-hover",
|
||||
"w-fit transition-all"
|
||||
"w-fit transition-[shadow,background]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -48,12 +59,12 @@ export const CardLayoutMini = ({
|
||||
"shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark",
|
||||
"rounded-docs_xs"
|
||||
)}
|
||||
width={45}
|
||||
height={36}
|
||||
width={imageDimensions.width}
|
||||
height={imageDimensions.height}
|
||||
alt={title || text || ""}
|
||||
style={{
|
||||
width: "45px",
|
||||
height: "36px",
|
||||
width: `${imageDimensions.width}px`,
|
||||
height: `${imageDimensions.height}px`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -64,12 +75,12 @@ export const CardLayoutMini = ({
|
||||
"shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark",
|
||||
"rounded-docs_xs"
|
||||
)}
|
||||
width={45}
|
||||
height={36}
|
||||
width={imageDimensions.width}
|
||||
height={imageDimensions.height}
|
||||
alt={title || text || ""}
|
||||
style={{
|
||||
width: "45px",
|
||||
height: "36px",
|
||||
width: `${imageDimensions.width}px`,
|
||||
height: `${imageDimensions.height}px`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -85,16 +96,28 @@ export const CardLayoutMini = ({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-medusa-fg-subtle">
|
||||
{isExternal ? <ArrowUpRightOnBox /> : <TriangleRightMini />}
|
||||
</span>
|
||||
{!closeable && (
|
||||
<span className="text-medusa-fg-subtle">
|
||||
{isExternal ? <ArrowUpRightOnBox /> : <TriangleRightMini />}
|
||||
</span>
|
||||
)}
|
||||
{href && (
|
||||
<Link
|
||||
href={href}
|
||||
className="absolute left-0 top-0 w-full h-full"
|
||||
className="absolute left-0 top-0 w-full h-full z-[1]"
|
||||
prefetch={false}
|
||||
{...hrefProps}
|
||||
/>
|
||||
)}
|
||||
{closeable && (
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
onClick={onClose}
|
||||
className="!p-[2.5px] z-[2] hover:!bg-transparent focus:!shadow-none focus:!bg-transparent"
|
||||
>
|
||||
<XMark />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IconProps } from "@medusajs/icons/dist/types"
|
||||
import { CardLargeLayout } from "./Layout/Large"
|
||||
import { CardFillerLayout } from "./Layout/Filler"
|
||||
import { CardLayoutMini } from "./Layout/Mini"
|
||||
import { LinkProps } from "next/link"
|
||||
|
||||
export type CardProps = {
|
||||
type?: "default" | "large" | "filler" | "mini"
|
||||
@@ -15,6 +16,10 @@ export type CardProps = {
|
||||
light: string
|
||||
dark: string
|
||||
}
|
||||
imageDimensions?: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
title?: string
|
||||
text?: string
|
||||
href?: string
|
||||
@@ -24,6 +29,9 @@ export type CardProps = {
|
||||
children?: React.ReactNode
|
||||
badge?: BadgeProps
|
||||
highlightText?: string[]
|
||||
closeable?: boolean
|
||||
onClose?: () => void
|
||||
hrefProps?: Partial<LinkProps & React.AllHTMLAttributes<HTMLAnchorElement>>
|
||||
}
|
||||
|
||||
export const Card = ({ type = "default", ...props }: CardProps) => {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import React from "react"
|
||||
import { MarkdownIcon } from "../../Icons/Markdown"
|
||||
import { useAiAssistant, useSiteConfig } from "../../../providers"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { BroomSparkle } from "@medusajs/icons"
|
||||
import { useAiAssistantChat } from "../../../providers/AiAssistant/Chat"
|
||||
|
||||
export const ContentMenuActions = () => {
|
||||
const {
|
||||
config: { baseUrl, basePath },
|
||||
} = useSiteConfig()
|
||||
const pathname = usePathname()
|
||||
const { setChatOpened } = useAiAssistant()
|
||||
const { setQuestion, loading } = useAiAssistantChat()
|
||||
const pageUrl = `${baseUrl}${basePath}${pathname}`
|
||||
|
||||
const handleAiAssistantClick = () => {
|
||||
if (loading) {
|
||||
return
|
||||
}
|
||||
setQuestion(`Explain the page ${pageUrl}`)
|
||||
setChatOpened(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-docs_0.5">
|
||||
<Link
|
||||
className="flex items-center gap-docs_0.5 text-medusa-fg-subtle text-x-small-plus hover:text-medusa-fg-base"
|
||||
href={`${pageUrl}/index.html.md`}
|
||||
>
|
||||
<MarkdownIcon width={15} height={15} />
|
||||
View as Markdown
|
||||
</Link>
|
||||
<button
|
||||
className="appearance-none p-0 flex items-center gap-docs_0.5 text-medusa-fg-subtle text-x-small-plus hover:text-medusa-fg-base"
|
||||
onClick={handleAiAssistantClick}
|
||||
>
|
||||
<BroomSparkle width={15} height={15} />
|
||||
Explain with AI Assistant
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
"use client"
|
||||
|
||||
import React, { useMemo } from "react"
|
||||
import { useSiteConfig } from "../../../providers"
|
||||
import { products } from "../../../constants"
|
||||
import { Product } from "types"
|
||||
import { BorderedIcon } from "../../BorderedIcon"
|
||||
import clsx from "clsx"
|
||||
|
||||
export const ContentMenuProducts = () => {
|
||||
const { frontmatter, config } = useSiteConfig()
|
||||
|
||||
const loadedProducts = useMemo(() => {
|
||||
return frontmatter.products
|
||||
?.sort()
|
||||
.map((product) => {
|
||||
return products.find(
|
||||
(p) => p.name.toLowerCase() === product.toLowerCase()
|
||||
)
|
||||
})
|
||||
.filter(Boolean) as Product[]
|
||||
}, [frontmatter.products])
|
||||
|
||||
if (!loadedProducts?.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
const getProductUrl = (product: Product) => {
|
||||
return `${config.baseUrl}${product.path}`
|
||||
}
|
||||
|
||||
const getProductImageUrl = (product: Product) => {
|
||||
return `${config.basePath}${product.image}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-docs_0.5">
|
||||
<span className="text-x-small-plus text-medusa-fg-muted">
|
||||
Modules used
|
||||
</span>
|
||||
{loadedProducts.map((product, index) => (
|
||||
<a
|
||||
key={index}
|
||||
href={getProductUrl(product)}
|
||||
className="flex gap-docs_0.5 items-center"
|
||||
>
|
||||
<BorderedIcon
|
||||
wrapperClassName={clsx("bg-medusa-bg-base")}
|
||||
icon={getProductImageUrl(product)}
|
||||
/>
|
||||
<span className="text-medusa-fg-subtle text-x-small-plus">
|
||||
{product.title}
|
||||
</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect } from "react"
|
||||
import { ToCItem, ToCItemUi } from "types"
|
||||
import {
|
||||
ActiveOnScrollItem,
|
||||
useActiveOnScroll,
|
||||
useScrollController,
|
||||
} from "../../../hooks"
|
||||
import clsx from "clsx"
|
||||
import Link from "next/link"
|
||||
import { useSiteConfig } from "../../../providers"
|
||||
import { Loading } from "../../Loading"
|
||||
|
||||
export const ContentMenuToc = () => {
|
||||
const { toc: items, frontmatter, setToc } = useSiteConfig()
|
||||
const { items: generatedItems, activeItemId } = useActiveOnScroll({
|
||||
maxLevel: 4,
|
||||
})
|
||||
|
||||
const formatHeadingContent = (content: string | null): string => {
|
||||
return content?.replaceAll(/#$/g, "") || ""
|
||||
}
|
||||
|
||||
const formatHeadingObject = ({
|
||||
heading,
|
||||
children,
|
||||
}: ActiveOnScrollItem): ToCItemUi => {
|
||||
const level = parseInt(heading.tagName.replace("H", ""))
|
||||
return {
|
||||
title: formatHeadingContent(heading.textContent),
|
||||
id: heading.id,
|
||||
level,
|
||||
children: children?.map(formatHeadingObject),
|
||||
associatedHeading: heading as HTMLHeadingElement,
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
frontmatter.generate_toc &&
|
||||
generatedItems &&
|
||||
items?.length !== generatedItems.length
|
||||
) {
|
||||
const tocItems: ToCItem[] = generatedItems.map(formatHeadingObject)
|
||||
setToc(tocItems)
|
||||
}
|
||||
}, [frontmatter, generatedItems])
|
||||
|
||||
useEffect(() => {
|
||||
const activeElement = document.querySelector(
|
||||
".toc-item a[href='#" + activeItemId + "']"
|
||||
) as HTMLAnchorElement
|
||||
if (!activeElement) {
|
||||
return
|
||||
}
|
||||
|
||||
activeElement.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
inline: "nearest",
|
||||
})
|
||||
}, [activeItemId])
|
||||
|
||||
if (items && !items.length) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-max max-h-full overflow-y-hidden flex relative flex-col">
|
||||
<div className="absolute -left-px top-0 h-full w-[1.5px] bg-medusa-border-base" />
|
||||
{items !== null && (
|
||||
<TocList
|
||||
items={items}
|
||||
activeItemId={activeItemId}
|
||||
className="relative overflow-y-auto"
|
||||
/>
|
||||
)}
|
||||
{items === null && <EmptyTocItems />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type TocListProps = {
|
||||
items: ToCItem[]
|
||||
activeItemId: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const TocList = ({ items, activeItemId, className }: TocListProps) => {
|
||||
return (
|
||||
<ul className={className}>
|
||||
{items.map((item) => (
|
||||
<TocItem item={item} key={item.id} activeItemId={activeItemId} />
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
type TocItemProps = {
|
||||
item: ToCItem
|
||||
activeItemId: string
|
||||
}
|
||||
|
||||
const TocItem = ({ item, activeItemId }: TocItemProps) => {
|
||||
const { scrollToElement } = useScrollController()
|
||||
return (
|
||||
<li className="w-full pt-docs_0.5 toc-item">
|
||||
<Link
|
||||
href={`#${item.id}`}
|
||||
className={clsx(
|
||||
"text-x-small-plus block w-full border-l-[1.5px]",
|
||||
item.id === activeItemId &&
|
||||
"border-medusa-fg-base text-medusa-fg-base",
|
||||
item.id !== activeItemId &&
|
||||
"text-medusa-fg-muted hover:text-medusa-fg-base border-transparent"
|
||||
)}
|
||||
style={{
|
||||
paddingLeft: `${(item.level - 1) * 12}px`,
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
history.pushState({}, "", `#${item.id}`)
|
||||
const elm = document.getElementById(item.id) as HTMLElement
|
||||
scrollToElement(elm)
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
{(item.children?.length ?? 0) > 0 && (
|
||||
<TocList items={item.children!} activeItemId={activeItemId} />
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
const EmptyTocItems = () => {
|
||||
return (
|
||||
<div className="animate-pulse">
|
||||
<Loading count={5} className="pt-docs_0.5 px-docs_0.75 !my-0" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { Card } from "../../Card"
|
||||
import { useIsBrowser, useSiteConfig } from "../../../providers"
|
||||
import clsx from "clsx"
|
||||
|
||||
const LOCAL_STORAGE_KEY = "last-version"
|
||||
|
||||
export const ContentMenuVersion = () => {
|
||||
const {
|
||||
config: { version },
|
||||
} = useSiteConfig()
|
||||
const [showNewVersion, setShowNewVersion] = useState(false)
|
||||
const { isBrowser } = useIsBrowser()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowser) {
|
||||
return
|
||||
}
|
||||
|
||||
const storedVersion = localStorage.getItem(LOCAL_STORAGE_KEY)
|
||||
if (storedVersion !== version.number) {
|
||||
setShowNewVersion(true)
|
||||
}
|
||||
}, [isBrowser])
|
||||
|
||||
const handleClose = () => {
|
||||
if (!showNewVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
setShowNewVersion(false)
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, version.number)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
type="mini"
|
||||
title={`New version`}
|
||||
text={`v${version.number} details`}
|
||||
closeable
|
||||
onClose={handleClose}
|
||||
href={version.releaseUrl}
|
||||
hrefProps={{
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
}}
|
||||
themeImage={version.bannerImage}
|
||||
imageDimensions={{
|
||||
width: 64,
|
||||
height: 40,
|
||||
}}
|
||||
className={clsx(
|
||||
"!border-0",
|
||||
(!showNewVersion || version.hide) && "invisible"
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client"
|
||||
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { ContentMenuVersion } from "./Version"
|
||||
import { ContentMenuToc } from "./Toc"
|
||||
import { ContentMenuActions } from "./Actions"
|
||||
import { ContentMenuProducts } from "./Products"
|
||||
import { useLayout } from "../../providers"
|
||||
|
||||
export const ContentMenu = () => {
|
||||
const { showCollapsedNavbar } = useLayout()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"hidden lg:flex w-full max-w-sidebar-lg",
|
||||
"flex-col gap-docs_2 pt-[28px] pb-docs_1.5 pr-docs_1",
|
||||
"fixed top-[57px] right-docs_0.25 z-10",
|
||||
showCollapsedNavbar && "h-[calc(100%-112px)]",
|
||||
!showCollapsedNavbar && "h-[calc(100%-56px)]"
|
||||
)}
|
||||
>
|
||||
<ContentMenuVersion />
|
||||
<div className="flex flex-col gap-docs_1.5 flex-1 overflow-auto">
|
||||
<ContentMenuToc />
|
||||
<ContentMenuActions />
|
||||
<ContentMenuProducts />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { LlmDropdown } from "../../LlmDropdown"
|
||||
|
||||
export type H1Props = React.HTMLAttributes<HTMLHeadingElement> & {
|
||||
id?: string
|
||||
hideLlmDropdown?: boolean
|
||||
}
|
||||
|
||||
export const H1 = ({ className, hideLlmDropdown, ...props }: H1Props) => {
|
||||
export const H1 = ({ className, ...props }: H1Props) => {
|
||||
return (
|
||||
<div className="flex items-start justify-between gap-2 h1-wrapper">
|
||||
<h1
|
||||
@@ -18,7 +16,6 @@ export const H1 = ({ className, hideLlmDropdown, ...props }: H1Props) => {
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{!hideLlmDropdown && <LlmDropdown />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect } from "react"
|
||||
import { FrontMatter, ToCItem } from "types"
|
||||
import { useSiteConfig } from "../../providers"
|
||||
|
||||
type InjectedMDXDataProps = {
|
||||
frontmatter: FrontMatter
|
||||
toc: ToCItem[]
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is injected by a recma plugin into MDX documents.
|
||||
*/
|
||||
export const InjectedMDXData = ({ frontmatter, toc }: InjectedMDXDataProps) => {
|
||||
const { setFrontmatter, setToc } = useSiteConfig()
|
||||
|
||||
useEffect(() => {
|
||||
setFrontmatter(frontmatter)
|
||||
}, [frontmatter])
|
||||
|
||||
useEffect(() => {
|
||||
setToc(toc)
|
||||
}, [toc])
|
||||
|
||||
return <></>
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export type LinkProps = Partial<NextLinkProps> &
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
withIcon?: boolean
|
||||
variant?: "default" | "content"
|
||||
}
|
||||
|
||||
export const Link = ({
|
||||
@@ -17,6 +18,7 @@ export const Link = ({
|
||||
children,
|
||||
className,
|
||||
withIcon = false,
|
||||
variant = "default",
|
||||
...rest
|
||||
}: LinkProps) => {
|
||||
if (href?.replace(/#.*$/, "").endsWith("page.mdx")) {
|
||||
@@ -27,7 +29,12 @@ export const Link = ({
|
||||
href={href || ""}
|
||||
{...rest}
|
||||
className={clsx(
|
||||
"text-medusa-fg-interactive hover:text-medusa-fg-interactive-hover",
|
||||
variant === "default" &&
|
||||
"text-medusa-fg-interactive hover:text-medusa-fg-interactive-hover",
|
||||
variant === "content" && [
|
||||
"border-b border-medusa-border-strong hover:border-medusa-fg-interactive",
|
||||
"transition-all duration-200",
|
||||
],
|
||||
withIcon && "flex gap-0.25 items-center group",
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React, { useRef, useState } from "react"
|
||||
import { useAiAssistant, useSiteConfig } from "../../providers"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { AiAssistent, Book } from "@medusajs/icons"
|
||||
import { DropdownMenu, Menu } from "../Menu"
|
||||
import { MarkdownIcon } from "../Icons/Markdown"
|
||||
import { useAiAssistantChat } from "../../providers/AiAssistant/Chat"
|
||||
import clsx from "clsx"
|
||||
import { useClickOutside } from "../.."
|
||||
|
||||
export const LlmDropdown = () => {
|
||||
const {
|
||||
config: { baseUrl, basePath },
|
||||
} = useSiteConfig()
|
||||
const pathname = usePathname()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { setChatOpened } = useAiAssistant()
|
||||
const { setQuestion, loading } = useAiAssistantChat()
|
||||
const ref = useRef<HTMLButtonElement | null>(null)
|
||||
useClickOutside({
|
||||
elmRef: ref,
|
||||
onClickOutside: () => {
|
||||
setOpen(false)
|
||||
},
|
||||
})
|
||||
|
||||
const pageUrl = `${baseUrl}${basePath}${pathname}`
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
dropdownButtonContent={<Book />}
|
||||
menuComponent={
|
||||
<Menu
|
||||
items={[
|
||||
{
|
||||
type: "link",
|
||||
title: "View as Markdown",
|
||||
link: `${pageUrl}/index.html.md`,
|
||||
icon: <MarkdownIcon width={19} height={19} />,
|
||||
openInNewTab: true,
|
||||
},
|
||||
{
|
||||
type: "action",
|
||||
title: "Ask AI Assistant",
|
||||
action: () => {
|
||||
if (loading) {
|
||||
return
|
||||
}
|
||||
setQuestion(`Explain the page ${pageUrl}`)
|
||||
setChatOpened(true)
|
||||
setOpen(false)
|
||||
},
|
||||
icon: <AiAssistent />,
|
||||
},
|
||||
]}
|
||||
className={clsx(
|
||||
"absolute right-0 top-[calc(100%+8px)] w-max",
|
||||
!open && "hidden"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
className="hidden md:block"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export const MainNavItemDropdown = ({
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"cursor-pointer flex gap-docs_0.25 items-center",
|
||||
"cursor-pointer flex gap-docs_0.25 items-center px-docs_0.25 py-docs_0.5",
|
||||
isActive && "text-medusa-fg-base",
|
||||
!isActive && [
|
||||
"text-medusa-fg-muted hover:text-medusa-fg-subtle",
|
||||
|
||||
@@ -1,63 +1,27 @@
|
||||
"use state"
|
||||
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { useIsBrowser, useSiteConfig } from "../../../providers"
|
||||
import React from "react"
|
||||
import { useSiteConfig } from "../../../providers"
|
||||
import Link from "next/link"
|
||||
import { Tooltip } from "../../Tooltip"
|
||||
import clsx from "clsx"
|
||||
|
||||
const LOCAL_STORAGE_SUFFIX = "last-version"
|
||||
|
||||
export const MainNavVersion = () => {
|
||||
const {
|
||||
config: { version },
|
||||
} = useSiteConfig()
|
||||
const [showNewBadge, setShowNewBadge] = useState(false)
|
||||
const { isBrowser } = useIsBrowser()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowser) {
|
||||
return
|
||||
}
|
||||
|
||||
const storedVersion = localStorage.getItem(LOCAL_STORAGE_SUFFIX)
|
||||
if (storedVersion !== version.number) {
|
||||
setShowNewBadge(true)
|
||||
}
|
||||
}, [isBrowser])
|
||||
|
||||
const afterHover = () => {
|
||||
if (!showNewBadge) {
|
||||
return
|
||||
}
|
||||
|
||||
setShowNewBadge(false)
|
||||
localStorage.setItem(LOCAL_STORAGE_SUFFIX, version.number)
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={version.releaseUrl}
|
||||
target="_blank"
|
||||
className={clsx(version.hide && "hidden")}
|
||||
className={clsx(version.hide && "hidden", "px-docs_0.5 py-docs_0.25")}
|
||||
>
|
||||
<Tooltip html="View the release notes<br/>on GitHub">
|
||||
<span
|
||||
className="relative text-compact-small-plus block"
|
||||
onMouseOut={afterHover}
|
||||
>
|
||||
<span className="relative text-compact-small-plus block">
|
||||
<span className="flex justify-center items-center">
|
||||
v{version.number}
|
||||
</span>
|
||||
{showNewBadge && (
|
||||
<span
|
||||
className={clsx(
|
||||
"bg-medusa-tag-blue-icon w-[10px] h-[10px]",
|
||||
"absolute -top-docs_0.25 -right-docs_0.5",
|
||||
"animate-pulse rounded-full"
|
||||
)}
|
||||
></span>
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
|
||||
@@ -71,14 +71,6 @@ export const MainNav = ({ className, itemsClassName }: MainNavProps) => {
|
||||
>
|
||||
<div className="lg:flex items-center gap-[6px] text-medusa-fg-subtle hidden">
|
||||
<MainNavVersion />
|
||||
<span
|
||||
className={clsx(
|
||||
"text-compact-small",
|
||||
config.version.hide && "hidden"
|
||||
)}
|
||||
>
|
||||
·
|
||||
</span>
|
||||
<MainNavItemDropdown
|
||||
item={{
|
||||
type: "dropdown",
|
||||
@@ -113,9 +105,6 @@ export const MainNav = ({ className, itemsClassName }: MainNavProps) => {
|
||||
className="text-medusa-fg-subtle"
|
||||
wrapperClassName="z-10"
|
||||
/>
|
||||
{!showCollapsedNavbar && (
|
||||
<span className={clsx("text-compact-small")}>·</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<AiAssistantTriggerButton />
|
||||
|
||||
@@ -6,7 +6,7 @@ import React, { useEffect, useMemo, useState } from "react"
|
||||
import { Sidebar } from "types"
|
||||
import { Badge, Loading, SidebarItem, useSidebar } from "../../../.."
|
||||
import clsx from "clsx"
|
||||
import { MinusMini, PlusMini } from "@medusajs/icons"
|
||||
import { TriangleDownMini, TriangleUpMini } from "@medusajs/icons"
|
||||
|
||||
export type SidebarItemCategoryProps = {
|
||||
item: Sidebar.SidebarItemCategory
|
||||
@@ -109,8 +109,8 @@ export const SidebarItemCategory = ({
|
||||
)}
|
||||
{!item.additionalElms && (
|
||||
<>
|
||||
{open && <MinusMini />}
|
||||
{!open && <PlusMini />}
|
||||
{open && <TriangleDownMini />}
|
||||
{!open && <TriangleUpMini />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import clsx from "clsx"
|
||||
import React, { useMemo } from "react"
|
||||
import { ToCItemUi } from "types"
|
||||
import { TocList } from "../List"
|
||||
|
||||
export type TocItemProps = {
|
||||
item: ToCItemUi
|
||||
activeItem: string
|
||||
}
|
||||
|
||||
export const TocItem = ({ item, activeItem }: TocItemProps) => {
|
||||
const isActive = useMemo(() => item.id === activeItem, [item, activeItem])
|
||||
|
||||
return (
|
||||
<li>
|
||||
<span
|
||||
className={clsx(
|
||||
"h-docs_0.125 rounded-full transition-colors",
|
||||
isActive && "bg-medusa-fg-subtle",
|
||||
!isActive && "bg-medusa-fg-disabled",
|
||||
item.level === 2 && "w-[20px]",
|
||||
item.level === 3 && "w-[10px]",
|
||||
"block"
|
||||
)}
|
||||
></span>
|
||||
{(item.children?.length || 0) > 0 && (
|
||||
<TocList items={item.children!} activeItem={activeItem} />
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { ToCItemUi } from "types"
|
||||
import { TocItem } from "../Item"
|
||||
|
||||
export type TocListProps = {
|
||||
items: ToCItemUi[]
|
||||
topLevel?: boolean
|
||||
activeItem: string
|
||||
}
|
||||
|
||||
export const TocList = ({
|
||||
items,
|
||||
topLevel = false,
|
||||
activeItem,
|
||||
}: TocListProps) => {
|
||||
return (
|
||||
<ul
|
||||
className={clsx(
|
||||
"flex flex-col gap-docs_0.75 items-end",
|
||||
!topLevel && "mt-docs_0.75"
|
||||
)}
|
||||
>
|
||||
{items.map((item, key) => (
|
||||
<TocItem item={item} key={key} activeItem={activeItem} />
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { EllipseMiniSolid } from "@medusajs/icons"
|
||||
import clsx from "clsx"
|
||||
import React from "react"
|
||||
import { ToCItemUi } from "types"
|
||||
import { Button, useScrollController } from "../../.."
|
||||
|
||||
export type TocMenuProps = {
|
||||
items: ToCItemUi[]
|
||||
activeItem: string
|
||||
show: boolean
|
||||
setShow: (value: boolean) => void
|
||||
}
|
||||
|
||||
export const TocMenu = ({ items, activeItem, show, setShow }: TocMenuProps) => {
|
||||
const { scrollToElement } = useScrollController()
|
||||
|
||||
const getItemElm = (item: ToCItemUi) => {
|
||||
const isActive = item.id === activeItem
|
||||
const hasChildren = item.children?.length || 0 > 0
|
||||
return (
|
||||
<li className={clsx("text-medusa-fg-base w-full")}>
|
||||
<Button
|
||||
variant="transparent-clear"
|
||||
className={clsx(
|
||||
"gap-docs_0.5 flex-1",
|
||||
"cursor-pointer rounded-docs_sm py-docs_0.25",
|
||||
"px-docs_0.5 hover:bg-medusa-bg-component-hover",
|
||||
"!text-inherit max-w-full w-full",
|
||||
"focus:!outline-none focus:!shadow-none focus:dark:!shadow-none",
|
||||
"!flex !justify-start !items-center",
|
||||
isActive && "!text-compact-small-plus",
|
||||
!isActive && "!text-compact-small"
|
||||
)}
|
||||
onClick={() => {
|
||||
history.pushState({}, "", `#${item.id}`)
|
||||
const elm = document.getElementById(item.id) as HTMLElement
|
||||
scrollToElement(elm)
|
||||
}}
|
||||
>
|
||||
<EllipseMiniSolid className={clsx(!isActive && "invisible")} />
|
||||
<span className="truncate flex-1 text-left">{item.title}</span>
|
||||
</Button>
|
||||
{hasChildren && (
|
||||
<ul className="pl-docs_0.5">
|
||||
{item.children!.map((childItem, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{getItemElm(childItem)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"hidden lg:flex relative transition-[width] lg:h-full",
|
||||
"w-0 z-50 bg-medusa-bg-subtle overflow-hidden flex flex-col justify-center",
|
||||
show && "lg:w-toc"
|
||||
)}
|
||||
onMouseLeave={() => setShow(false)}
|
||||
>
|
||||
<ul
|
||||
className={clsx(
|
||||
"p-docs_0.75 lg:w-toc max-h-full overflow-y-scroll",
|
||||
"absolute lg:-right-full transition-[right,opacity] opacity-0",
|
||||
show && "lg:right-0 lg:opacity-100"
|
||||
)}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<React.Fragment key={index}>{getItemElm(item)}</React.Fragment>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { ToCItemUi } from "types"
|
||||
import {
|
||||
ActiveOnScrollItem,
|
||||
isElmWindow,
|
||||
useActiveOnScroll,
|
||||
useIsBrowser,
|
||||
useLayout,
|
||||
useScrollController,
|
||||
} from "../.."
|
||||
import { TocList } from "./List"
|
||||
import clsx from "clsx"
|
||||
import { TocMenu } from "./Menu"
|
||||
|
||||
export const Toc = () => {
|
||||
const [items, setItems] = useState<ToCItemUi[]>([])
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const { isBrowser } = useIsBrowser()
|
||||
const { items: headingItems, activeItemId } = useActiveOnScroll({})
|
||||
const [maxHeight, setMaxHeight] = useState(0)
|
||||
const { scrollableElement } = useScrollController()
|
||||
const { showCollapsedNavbar } = useLayout()
|
||||
|
||||
const formatHeadingContent = (content: string | null): string => {
|
||||
return content?.replaceAll(/#$/g, "") || ""
|
||||
}
|
||||
|
||||
const formatHeadingObject = ({
|
||||
heading,
|
||||
children,
|
||||
}: ActiveOnScrollItem): ToCItemUi => {
|
||||
const level = parseInt(heading.tagName.replace("H", ""))
|
||||
return {
|
||||
title: formatHeadingContent(heading.textContent),
|
||||
id: heading.id,
|
||||
level,
|
||||
children: children?.map(formatHeadingObject),
|
||||
associatedHeading: heading as HTMLHeadingElement,
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setItems(headingItems.map(formatHeadingObject))
|
||||
}, [headingItems])
|
||||
|
||||
const handleResize = () => {
|
||||
const extraMargin = showCollapsedNavbar ? 112 : 56
|
||||
const offset =
|
||||
(scrollableElement instanceof HTMLElement
|
||||
? scrollableElement.offsetTop
|
||||
: 0) + extraMargin
|
||||
|
||||
setMaxHeight(
|
||||
(isElmWindow(scrollableElement)
|
||||
? scrollableElement.innerHeight
|
||||
: scrollableElement?.clientHeight || 0) - offset
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowser) {
|
||||
return
|
||||
}
|
||||
|
||||
handleResize()
|
||||
|
||||
window.addEventListener("resize", handleResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize)
|
||||
}
|
||||
}, [isBrowser])
|
||||
|
||||
return (
|
||||
<div className="hidden lg:block" onMouseOver={() => setShowMenu(true)}>
|
||||
<div
|
||||
className={clsx(
|
||||
"fixed top-1/2 right-[20px]",
|
||||
"hidden lg:flex justify-center items-center",
|
||||
"overflow-hidden z-10",
|
||||
showMenu && "lg:hidden",
|
||||
maxHeight < 1000 && "-translate-y-[40%]",
|
||||
maxHeight >= 1000 && "-translate-y-1/2"
|
||||
)}
|
||||
onMouseOver={() => setShowMenu(true)}
|
||||
style={{
|
||||
maxHeight,
|
||||
}}
|
||||
>
|
||||
<TocList items={items} topLevel={true} activeItem={activeItemId} />
|
||||
</div>
|
||||
<TocMenu
|
||||
items={items}
|
||||
activeItem={activeItemId}
|
||||
show={showMenu}
|
||||
setShow={setShowMenu}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -31,6 +31,7 @@ export * from "./Heading"
|
||||
export * from "./HooksLoader"
|
||||
export * from "./IconHeadline"
|
||||
export * from "./Icons"
|
||||
export * from "./InjectedMDXData"
|
||||
export * from "./InlineIcon"
|
||||
export * from "./InlineThemeImage"
|
||||
export * from "./InlineCode"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NavigationItem } from "types"
|
||||
import { NavigationItem, Product } from "types"
|
||||
|
||||
export const GITHUB_ISSUES_LINK =
|
||||
"https://github.com/medusajs/medusa/issues/new/choose"
|
||||
@@ -355,3 +355,132 @@ export const navDropdownItems: NavigationItem[] = [
|
||||
link: "/user-guide",
|
||||
},
|
||||
]
|
||||
|
||||
export const products: Product[] = [
|
||||
{
|
||||
type: "commerce",
|
||||
name: "api key",
|
||||
path: "/resources/commerce-modules/api-key",
|
||||
title: "API Key",
|
||||
image: "/images/api-key-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "auth",
|
||||
path: "/resources/commerce-modules/auth",
|
||||
title: "Auth",
|
||||
image: "/images/auth-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "cart",
|
||||
path: "/resources/commerce-modules/cart",
|
||||
title: "Cart",
|
||||
image: "/images/cart-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "currency",
|
||||
path: "/resources/commerce-modules/currency",
|
||||
title: "Currency",
|
||||
image: "/images/currency-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "customer",
|
||||
path: "/resources/commerce-modules/customer",
|
||||
title: "Customer",
|
||||
image: "/images/customer-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "fulfillment",
|
||||
path: "/resources/commerce-modules/fulfillment",
|
||||
title: "Fulfillment",
|
||||
image: "/images/fulfillment-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "inventory",
|
||||
path: "/resources/commerce-modules/inventory",
|
||||
title: "Inventory",
|
||||
image: "/images/inventory-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "order",
|
||||
path: "/resources/commerce-modules/order",
|
||||
title: "Order",
|
||||
image: "/images/order-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "payment",
|
||||
path: "/resources/commerce-modules/payment",
|
||||
title: "Payment",
|
||||
image: "/images/payment-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "pricing",
|
||||
path: "/resources/commerce-modules/pricing",
|
||||
title: "Pricing",
|
||||
image: "/images/pricing-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "product",
|
||||
path: "/resources/commerce-modules/product",
|
||||
title: "Product",
|
||||
image: "/images/product-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "promotion",
|
||||
path: "/resources/commerce-modules/promotion",
|
||||
title: "Promotion",
|
||||
image: "/images/promotion-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "region",
|
||||
path: "/resources/commerce-modules/region",
|
||||
title: "Region",
|
||||
image: "/images/region-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "sales channel",
|
||||
path: "/resources/commerce-modules/sales-channel",
|
||||
title: "Sales Channel",
|
||||
image: "/images/sales-channel-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "stock location",
|
||||
path: "/resources/commerce-modules/stock-location",
|
||||
title: "Stock Location",
|
||||
image: "/images/stock-location-icon.png",
|
||||
},
|
||||
// TODO need an image for this one
|
||||
// {
|
||||
// type: "commerce",
|
||||
// name: "store",
|
||||
// title: "Store",
|
||||
// image: "/images/store-icon.png",
|
||||
// },
|
||||
{
|
||||
type: "commerce",
|
||||
name: "tax",
|
||||
path: "/resources/commerce-modules/tax",
|
||||
title: "Tax",
|
||||
image: "/images/tax-icon.png",
|
||||
},
|
||||
{
|
||||
type: "commerce",
|
||||
name: "user",
|
||||
path: "/resources/commerce-modules/user",
|
||||
title: "User",
|
||||
image: "/images/user-icon.png",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -130,6 +130,29 @@ export const useActiveOnScroll = ({
|
||||
? scrollableElement.scrollHeight / 2
|
||||
: 0
|
||||
|
||||
if (scrollableElement?.scrollTop === 0) {
|
||||
// set the first heading as active if the scrollable element is at the top
|
||||
setActiveItemId(
|
||||
items.length && useDefaultIfNoActive ? items[0].heading.id : ""
|
||||
)
|
||||
|
||||
return
|
||||
} else if (
|
||||
scrollableElement?.scrollTop + scrollableElement?.clientHeight >=
|
||||
scrollableElement?.scrollHeight
|
||||
) {
|
||||
// set the last heading as active if the scrollable element is at the bottom
|
||||
let lastHeading = items[items.length - 1]
|
||||
while (lastHeading?.children?.length) {
|
||||
lastHeading = lastHeading.children[lastHeading.children.length - 1]
|
||||
}
|
||||
setActiveItemId(
|
||||
lastHeading && useDefaultIfNoActive ? lastHeading.heading.id : ""
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
headings?.forEach((heading) => {
|
||||
if (heading.id === hash) {
|
||||
selectedHeadingByHash = heading as HTMLHeadingElement
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
|
||||
import React, { useEffect } from "react"
|
||||
import clsx from "clsx"
|
||||
import { MainNav, useIsBrowser, useLayout, useSidebar } from ".."
|
||||
import { MainNav, useIsBrowser, useLayout, useSidebar, useSiteConfig } from ".."
|
||||
import { ContentMenu } from "../components/ContentMenu"
|
||||
|
||||
export type MainContentLayoutProps = {
|
||||
mainWrapperClasses?: string
|
||||
contentClassName?: string
|
||||
children: React.ReactNode
|
||||
showContentMenu?: boolean
|
||||
}
|
||||
|
||||
export const MainContentLayout = ({
|
||||
children,
|
||||
mainWrapperClasses,
|
||||
contentClassName,
|
||||
showContentMenu = true,
|
||||
}: MainContentLayoutProps) => {
|
||||
const { isBrowser } = useIsBrowser()
|
||||
const { desktopSidebarOpen } = useSidebar()
|
||||
const { mainContentRef } = useLayout()
|
||||
const { frontmatter } = useSiteConfig()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isBrowser) {
|
||||
@@ -36,7 +40,7 @@ export const MainContentLayout = ({
|
||||
"relative max-w-full",
|
||||
"h-full flex-1",
|
||||
"flex flex-col",
|
||||
"gap-docs_0.5 lg:pt-docs_0.25 lg:mr-docs_0.25 scroll-m-docs_0.25",
|
||||
"gap-docs_0.5 lg:py-docs_0.25 lg:mr-docs_0.25 scroll-m-docs_0.25",
|
||||
!desktopSidebarOpen && "lg:ml-docs_0.25",
|
||||
mainWrapperClasses
|
||||
)}
|
||||
@@ -47,7 +51,7 @@ export const MainContentLayout = ({
|
||||
"flex-col items-center",
|
||||
"h-full w-full",
|
||||
"overflow-y-scroll overflow-x-hidden",
|
||||
"md:rounded-t-docs_DEFAULT",
|
||||
"md:rounded-docs_DEFAULT",
|
||||
"shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark",
|
||||
mainWrapperClasses
|
||||
)}
|
||||
@@ -57,14 +61,16 @@ export const MainContentLayout = ({
|
||||
<MainNav />
|
||||
<div
|
||||
className={clsx(
|
||||
"flex justify-center",
|
||||
"pt-docs_4 lg:pt-docs_6 pb-docs_8 lg:pb-docs_4",
|
||||
showContentMenu &&
|
||||
"grid grid-cols-1 lg:mx-auto lg:grid-cols-[1fr_221px]",
|
||||
contentClassName
|
||||
)}
|
||||
id="content"
|
||||
>
|
||||
{children}
|
||||
<div className="flex justify-center">{children}</div>
|
||||
</div>
|
||||
{showContentMenu && !frontmatter.hide_content_menu && <ContentMenu />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from "react"
|
||||
import clsx from "clsx"
|
||||
import { RootProviders, Sidebar, SidebarProps } from "@/components"
|
||||
import { Toc } from "../components/Toc"
|
||||
import { MainContentLayout, MainContentLayoutProps } from "./main-content"
|
||||
import { AiAssistantChatWindow } from "../components/AiAssistant/ChatWindow"
|
||||
|
||||
export type RootLayoutProps = {
|
||||
bodyClassName?: string
|
||||
showToc?: boolean
|
||||
sidebarProps?: SidebarProps
|
||||
showBreadcrumbs?: boolean
|
||||
ProvidersComponent: React.FC<{ children: React.ReactNode }>
|
||||
@@ -17,7 +15,6 @@ export type RootLayoutProps = {
|
||||
export const RootLayout = ({
|
||||
bodyClassName,
|
||||
sidebarProps,
|
||||
showToc = true,
|
||||
ProvidersComponent,
|
||||
...mainProps
|
||||
}: RootLayoutProps) => {
|
||||
@@ -36,7 +33,6 @@ export const RootLayout = ({
|
||||
<Sidebar {...sidebarProps} />
|
||||
<div className={clsx("relative", "h-screen", "flex")}>
|
||||
<MainContentLayout {...mainProps} />
|
||||
{showToc && <Toc />}
|
||||
<AiAssistantChatWindow />
|
||||
</div>
|
||||
</ProvidersComponent>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const LayoutProvider = ({
|
||||
const [showCollapsedNavbar, setShowCollapsedNavbar] = useState(false)
|
||||
|
||||
useResizeObserver(mainContentRef as React.RefObject<HTMLDivElement>, () => {
|
||||
if (disableResizeObserver || window.innerWidth < 1100) {
|
||||
if (disableResizeObserver || window.innerWidth < 1024) {
|
||||
setShowCollapsedNavbar(false)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import React, { createContext, useContext, useState } from "react"
|
||||
import { DocsConfig } from "types"
|
||||
import { DocsConfig, FrontMatter, ToCItem } from "types"
|
||||
import { globalConfig } from "../../global-config"
|
||||
import { GITHUB_ISSUES_LINK } from "../.."
|
||||
|
||||
export type SiteConfigContextType = {
|
||||
config: DocsConfig
|
||||
setConfig: React.Dispatch<React.SetStateAction<DocsConfig>>
|
||||
frontmatter: FrontMatter
|
||||
setFrontmatter: React.Dispatch<React.SetStateAction<FrontMatter>>
|
||||
toc: ToCItem[] | null
|
||||
setToc: React.Dispatch<React.SetStateAction<ToCItem[] | null>>
|
||||
}
|
||||
|
||||
const SiteConfigContext = createContext<SiteConfigContextType | null>(null)
|
||||
@@ -37,12 +41,18 @@ export const SiteConfigProvider = ({
|
||||
initConfig || {}
|
||||
)
|
||||
)
|
||||
const [frontmatter, setFrontmatter] = useState<FrontMatter>({})
|
||||
const [toc, setToc] = useState<ToCItem[] | null>(null)
|
||||
|
||||
return (
|
||||
<SiteConfigContext.Provider
|
||||
value={{
|
||||
config,
|
||||
setConfig,
|
||||
frontmatter,
|
||||
setFrontmatter,
|
||||
toc,
|
||||
setToc,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user