docs: create docs workspace (#5174)

* docs: migrate ui docs to docs universe

* created yarn workspace

* added eslint and tsconfig configurations

* fix eslint configurations

* fixed eslint configurations

* shared tailwind configurations

* added shared ui package

* added more shared components

* migrating more components

* made details components shared

* move InlineCode component

* moved InputText

* moved Loading component

* Moved Modal component

* moved Select components

* Moved Tooltip component

* moved Search components

* moved ColorMode provider

* Moved Notification components and providers

* used icons package

* use UI colors in api-reference

* moved Navbar component

* used Navbar and Search in UI docs

* added Feedback to UI docs

* general enhancements

* fix color mode

* added copy colors file from ui-preset

* added features and enhancements to UI docs

* move Sidebar component and provider

* general fixes and preparations for deployment

* update docusaurus version

* adjusted versions

* fix output directory

* remove rootDirectory property

* fix yarn.lock

* moved code component

* added vale for all docs MD and MDX

* fix tests

* fix vale error

* fix deployment errors

* change ignore commands

* add output directory

* fix docs test

* general fixes

* content fixes

* fix announcement script

* added changeset

* fix vale checks

* added nofilter option

* fix vale error
This commit is contained in:
Shahed Nasser
2023-09-21 20:57:15 +03:00
committed by GitHub
parent 19c5d5ba36
commit fa7c94b4cc
3209 changed files with 32188 additions and 31018 deletions

View File

@@ -0,0 +1,188 @@
import React, { type ReactNode } from "react"
import clsx from "clsx"
import Translate from "@docusaurus/Translate"
import type { Props } from "@theme/Admonition"
import {
ExclamationCircleSolid,
InformationCircleSolid,
LightBulbSolid,
} from "@medusajs/icons"
function NoteIcon() {
return (
<InformationCircleSolid className="inline-block mr-0.125 text-medusa-fg-interactive-dark" />
)
}
function TipIcon() {
return (
<LightBulbSolid className="inline-block mr-0.125 text-medusa-tag-orange-icon-dark" />
)
}
function DangerIcon() {
return (
<ExclamationCircleSolid className="inline-block mr-0.125 text-medusa-fg-error dark:text-medusa-fg-error-dark" />
)
}
function InfoIcon() {
return NoteIcon()
}
function CautionIcon() {
return DangerIcon()
}
type AdmonitionConfig = {
iconComponent: React.ComponentType
infimaClassName: string
label: ReactNode
}
// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
const AdmonitionConfigs: Record<Props["type"], AdmonitionConfig> = {
note: {
infimaClassName: "secondary",
iconComponent: NoteIcon,
label: (
<Translate
id="theme.admonition.note"
description="The default label used for the Note admonition (:::note)"
>
note
</Translate>
),
},
tip: {
infimaClassName: "success",
iconComponent: TipIcon,
label: (
<Translate
id="theme.admonition.tip"
description="The default label used for the Tip admonition (:::tip)"
>
tip
</Translate>
),
},
danger: {
infimaClassName: "danger",
iconComponent: DangerIcon,
label: (
<Translate
id="theme.admonition.danger"
description="The default label used for the Danger admonition (:::danger)"
>
danger
</Translate>
),
},
info: {
infimaClassName: "info",
iconComponent: InfoIcon,
label: (
<Translate
id="theme.admonition.info"
description="The default label used for the Info admonition (:::info)"
>
info
</Translate>
),
},
caution: {
infimaClassName: "warning",
iconComponent: CautionIcon,
label: (
<Translate
id="theme.admonition.caution"
description="The default label used for the Caution admonition (:::caution)"
>
caution
</Translate>
),
},
}
// Legacy aliases, undocumented but kept for retro-compatibility
const aliases = {
secondary: "note",
important: "info",
success: "tip",
warning: "danger",
} as const
function getAdmonitionConfig(unsafeType: string): AdmonitionConfig {
const type =
(aliases as { [key: string]: Props["type"] })[unsafeType] ?? unsafeType
const config = (AdmonitionConfigs as { [key: string]: AdmonitionConfig })[
type
]
if (config) {
return config
}
console.warn(
`No admonition config found for admonition type "${type}". Using Info as fallback.`
)
return AdmonitionConfigs.info
}
// Workaround because it's difficult in MDX v1 to provide a MDX title as props
// See https://github.com/facebook/docusaurus/pull/7152#issuecomment-1145779682
function extractMDXAdmonitionTitle(children: ReactNode): {
mdxAdmonitionTitle: ReactNode | undefined
rest: ReactNode
} {
const items = React.Children.toArray(children)
const mdxAdmonitionTitle = items.find(
(item) =>
React.isValidElement(item) &&
(item.props as { mdxType: string } | null)?.mdxType ===
"mdxAdmonitionTitle"
)
const rest = <>{items.filter((item) => item !== mdxAdmonitionTitle)}</>
return {
mdxAdmonitionTitle,
rest,
}
}
function processAdmonitionProps(props: Props): Props {
const { mdxAdmonitionTitle, rest } = extractMDXAdmonitionTitle(props.children)
return {
...props,
title: props.title ?? mdxAdmonitionTitle,
children: rest,
}
}
export default function Admonition(props: Props): JSX.Element {
const { children, type, icon: iconProp } = processAdmonitionProps(props)
const typeConfig = getAdmonitionConfig(type)
const { iconComponent: IconComponent } = typeConfig
const icon = iconProp ?? <IconComponent />
return (
<div
className={clsx(
"p-1 border border-solid border-medusa-border-base dark:border-medusa-border-base-dark rounded",
"bg-medusa-bg-subtle dark:bg-medusa-bg-base-dark shadow-none",
"[&_a]:no-underline [&_a]:text-medusa-fg-interactive dark:[&_a]:text-medusa-fg-interactive-dark hover:[&_a]:text-medusa-fg-interactive-hover dark:hover:[&_a]:text-medusa-fg-interactive-hover-dark",
"mb-2 alert"
)}
>
<div className={clsx("flex")}>
<span className={clsx("inline-block h-1.5 w-1.5 mr-1")}>{icon}</span>
<div
className={clsx(
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
"text-medium flex-1 [&>*:last-child]:mb-0",
"[&>p>code]:px-0.5 [&>p>code]:text-code-label"
)}
>
{children}
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,33 @@
import React from "react"
import clsx from "clsx"
import { translate } from "@docusaurus/Translate"
import IconClose from "../../Icon/Close"
import type { Props } from "@theme/AnnouncementBar/CloseButton"
export default function AnnouncementBarCloseButton(
props: Props
): JSX.Element | null {
return (
<button
type="button"
aria-label={translate({
id: "theme.AnnouncementBar.closeButtonAriaLabel",
message: "Close",
description: "The ARIA label for close button of announcement bar",
})}
{...props}
className={clsx(
"p-0 leading-[0] self-start opacity-100 hover:opacity-100",
"bg-transparent border-0 cursor-pointer",
props.className
)}
>
<IconClose
width={20}
height={20}
strokeWidth={1.5}
className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark"
/>
</button>
)
}

View File

@@ -0,0 +1,29 @@
import React from "react"
import clsx from "clsx"
import { useThemeConfig } from "@docusaurus/theme-common"
import type { Props } from "@theme/AnnouncementBar/Content"
export default function AnnouncementBarContent(
props: Props
): JSX.Element | null {
const { announcementBar } = useThemeConfig()
const { content } = announcementBar!
return (
<div
className={clsx(
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
"text-compact-x-small-plus",
props.className
)}
>
<div
{...props}
className={clsx("text-medusa-fg-base dark:text-medusa-fg-base-dark")}
// Developer provided the HTML, so assume it's safe.
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: content }}
/>
<span>Read more</span>
</div>
)
}

View File

@@ -0,0 +1,50 @@
import React from "react"
import { useThemeConfig } from "@docusaurus/theme-common"
import { useAnnouncementBar } from "@docusaurus/theme-common/internal"
import AnnouncementBarCloseButton from "@theme/AnnouncementBar/CloseButton"
import AnnouncementBarContent from "@theme/AnnouncementBar/Content"
import clsx from "clsx"
import { Bordered } from "docs-ui"
import { BellAlertSolid } from "@medusajs/icons"
export default function AnnouncementBar(): JSX.Element | null {
const { announcementBar } = useThemeConfig()
const { isActive, close } = useAnnouncementBar()
if (!isActive) {
return null
}
const { isCloseable, id } = announcementBar!
return (
<div
className={clsx(
"relative flex items-center h-auto bg-medusa-bg-subtle dark:bg-medusa-bg-base-dark p-0.75",
"rounded mx-1.5 mb-1 shadow-card-rest dark:shadow-card-rest-dark",
"transition-all duration-200 ease-ease",
"hover:bg-medusa-bg-subtle-hover dark:hover:bg-medusa-bg-base-hover-dark",
"print:hidden"
)}
>
<Bordered wrapperClassName="mr-0.75">
<div
className={clsx(
"p-[6px] flex justify-center items-center",
"rounded-xs bg-medusa-bg-component dark:bg-medusa-bg-component-dark"
)}
>
<BellAlertSolid className="text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark" />
</div>
</Bordered>
<AnnouncementBarContent className={clsx("flex-1")} />
{isCloseable && (
<AnnouncementBarCloseButton
onClick={close}
className={clsx("z-[101] text-right lg:basis-[50px]")}
/>
)}
<a
href={id}
className={clsx("absolute top-0 left-0 w-full h-full z-[100]")}
/>
</div>
)
}

View File

@@ -0,0 +1,144 @@
import React from "react"
import clsx from "clsx"
import { useThemeConfig, usePrismTheme } from "@docusaurus/theme-common"
import {
parseCodeBlockTitle,
parseLanguage,
parseLines,
containsLineNumbers,
useCodeWordWrap,
} from "@docusaurus/theme-common/internal"
import Highlight, { defaultProps, type Language } from "prism-react-renderer"
import Line from "@theme/CodeBlock/Line"
import Container from "@theme/CodeBlock/Container"
import type { Props } from "@theme/CodeBlock/Content/String"
import useIsBrowser from "@docusaurus/useIsBrowser"
import { ThemeConfig } from "@medusajs/docs"
import { CopyButton, Tooltip } from "docs-ui"
import { ExclamationCircleSolid, SquareTwoStackSolid } from "@medusajs/icons"
export default function CodeBlockString({
children,
className: blockClassName = "",
metastring,
title: titleProp,
showLineNumbers: showLineNumbersProp,
language: languageProp,
noReport = false,
noCopy = false,
}: Props): JSX.Element {
const {
prism: { defaultLanguage, magicComments },
reportCodeLinkPrefix = "",
} = useThemeConfig() as ThemeConfig
const language =
languageProp ?? parseLanguage(blockClassName) ?? defaultLanguage
const prismTheme = usePrismTheme()
const wordWrap = useCodeWordWrap()
const isBrowser = useIsBrowser()
// We still parse the metastring in case we want to support more syntax in the
// future. Note that MDX doesn't strip quotes when parsing metastring:
// "title=\"xyz\"" => title: "\"xyz\""
const title = parseCodeBlockTitle(metastring) || titleProp
const { lineClassNames, code } = parseLines(children, {
metastring,
language,
magicComments,
})
const showLineNumbers = showLineNumbersProp ?? containsLineNumbers(metastring)
return (
<Container
as="div"
className={clsx(
blockClassName,
language &&
!blockClassName.includes(`language-${language}`) &&
`language-${language}`
)}
>
{title && <div>{title}</div>}
<div className={clsx("relative rounded-[inherit]")}>
<Highlight
{...defaultProps}
theme={prismTheme}
code={code}
language={(language ?? "text") as Language}
>
{({ className, tokens, getLineProps, getTokenProps }) => (
<>
<pre
tabIndex={0}
ref={wordWrap.codeBlockRef}
className={clsx("m-0 p-0", "thin-scrollbar", className)}
>
<code
className={clsx(
"font-[inherit] float-left min-w-full print:whitespace-pre-wrap",
showLineNumbers &&
tokens.length > 1 &&
"table p-1 code-block-numbering",
title && "p-1",
!title && tokens.length > 1 && "p-1",
!title && tokens.length === 1 && "py-0.5 pr-0.5 pl-1"
)}
>
{tokens.map((line, i) => (
<Line
key={i}
line={line}
getLineProps={getLineProps}
getTokenProps={getTokenProps}
classNames={lineClassNames[i]}
showLineNumbers={showLineNumbers && tokens.length > 1}
/>
))}
</code>
</pre>
<div
className={clsx(
"flex gap-x-0.125 absolute right-1",
tokens.length === 1 && "top-0.25",
tokens.length > 1 && "top-1"
)}
>
{!noReport && (
<Tooltip text="Report Incorrect Code">
<a
href={`${reportCodeLinkPrefix}&title=${encodeURIComponent(
`Docs(Code Issue): Code Issue in ${
isBrowser ? location.pathname : ""
}`
)}`}
target="_blank"
className={clsx(
"bg-transparent border-none p-0.25 cursor-pointer rounded",
"hover:bg-medusa-code-bg-base dark:hover:bg-medusa-code-bg-base-dark [&:not(:first-child)]:ml-0.5",
"inline-flex justify-center items-center invisible xs:visible"
)}
rel="noreferrer"
>
<ExclamationCircleSolid className="text-medusa-code-icon dark:text-medusa-code-icon-dark" />
</a>
</Tooltip>
)}
{!noCopy && (
<CopyButton
buttonClassName={clsx(
"flex bg-transparent border-none p-0.25 cursor-pointer rounded"
)}
text={code}
>
<SquareTwoStackSolid className="text-medusa-code-icon dark:text-medusa-code-icon-dark" />
</CopyButton>
)}
</div>
</>
)}
</Highlight>
</div>
</Container>
)
}

View File

@@ -0,0 +1,59 @@
import React, { isValidElement, type ReactNode } from "react"
import useIsBrowser from "@docusaurus/useIsBrowser"
import ElementContent from "@theme/CodeBlock/Content/Element"
import StringContent from "@theme/CodeBlock/Content/String"
import type { Props } from "@theme/CodeBlock"
import clsx from "clsx"
/**
* Best attempt to make the children a plain string so it is copyable. If there
* are react elements, we will not be able to copy the content, and it will
* return `children` as-is; otherwise, it concatenates the string children
* together.
*/
function maybeStringifyChildren(children: ReactNode): ReactNode {
if (React.Children.toArray(children).some((el) => isValidElement(el))) {
return children
}
// The children is now guaranteed to be one/more plain strings
return Array.isArray(children) ? children.join("") : (children as string)
}
export default function CodeBlock({
children: rawChildren,
noReport = false,
noCopy = false,
...props
}: Props): JSX.Element {
// The Prism theme on SSR is always the default theme but the site theme can
// be in a different mode. React hydration doesn't update DOM styles that come
// from SSR. Hence force a re-render after mounting to apply the current
// relevant styles.
const isBrowser = useIsBrowser()
const children = maybeStringifyChildren(rawChildren)
const CodeBlockComp =
typeof children === "string" ? StringContent : ElementContent
const title = props.title
delete props.title
return (
<div className="code-wrapper">
{title && <div className="code-header">{title}</div>}
<CodeBlockComp
key={String(isBrowser)}
noReport={noReport}
noCopy={noCopy}
{...props}
className={clsx(
!title && "rounded",
title && "!rounded-t-none !rounded-b",
props.className
)}
showLineNumbers={true}
>
{children as string}
</CodeBlockComp>
</div>
)
}

View File

@@ -0,0 +1,26 @@
import React from "react"
import {
Details as UiDetails,
type DetailsProps as UiDetailsProps,
} from "docs-ui"
export type DetailsProps = {
summary: React.ReactNode
children?: React.ReactNode
} & UiDetailsProps
export default function Details({
summary,
children,
...props
}: DetailsProps): JSX.Element {
return (
<UiDetails
{...props}
summaryContent={!React.isValidElement(summary) ? summary : undefined}
summaryElm={React.isValidElement(summary) ? summary : undefined}
>
{children}
</UiDetails>
)
}

View File

@@ -0,0 +1,247 @@
import React, { type ReactNode } from "react"
import clsx from "clsx"
import Link from "@docusaurus/Link"
import {
findFirstCategoryLink,
useDocById,
} from "@docusaurus/theme-common/internal"
import isInternalUrl from "@docusaurus/isInternalUrl"
import { translate } from "@docusaurus/Translate"
import {
ModifiedDocCard,
ModifiedDocCardItemCategory,
ModifiedDocCardItemLink,
ModifiedSidebarItem,
} from "@medusajs/docs"
import { Badge } from "docs-ui"
import BorderedIcon from "../../components/BorderedIcon"
import Icons from "../Icon"
type ModifiedProps = {
item: ModifiedDocCard
}
function CardContainer({
href,
children,
className,
}: {
href: string
children: ReactNode
className?: string
}): JSX.Element {
return (
<article className={`card-wrapper margin-bottom--lg`}>
<Link
href={href}
className={clsx(
"card",
"bg-medusa-bg-subtle dark:bg-medusa-bg-base-dark",
"rounded shadow-card-rest dark:shadow-card-rest-dark",
"transition-all duration-200 ease-ease",
"flex p-1 !pb-1.5 h-full",
className
)}
>
{children}
</Link>
</article>
)
}
function CardLayout({
href,
icon,
title,
description,
html,
containerClassName,
isSoon = false,
badge,
}: ModifiedDocCard): JSX.Element {
const isHighlighted = containerClassName?.includes("card-highlighted")
return (
<CardContainer
href={href}
className={clsx(
containerClassName,
!isSoon &&
"hover:bg-medusa-bg-subtle-hover dark:hover:bg-medusa-bg-base-hover-dark",
isSoon && "pointer-events-none",
isHighlighted &&
"md:before:content-[''] md:before:absolute md:before:top-0 before:right-0 md:before:w-1/2 md:before:h-full md:before:bg-no-repeat md:before:bg-cover md:before:bg-card-highlighted dark:md:before:bg-card-highlighted-dark",
!isSoon && "hover:shadow-card-hover dark:hover:shadow-card-hover-dark"
)}
>
<div className={clsx("mb-1 flex justify-between items-center")}>
{icon}
{isSoon && <Badge variant={"purple"}>Guide coming soon</Badge>}
{badge && <Badge {...badge} />}
</div>
<div className={clsx("w-[calc(100%-20px)] [&>*:last-child]:mb-0")}>
<span
className={clsx(
"text-compact-medium-plus text-medusa-fg-base dark:text-medusa-fg-base-dark",
"mb-0.25 block",
"transition-all duration-200 ease-ease",
isSoon &&
"group-hover:text-medusa-fg-disabled dark:group-hover:text-medusa-fg-disabled-dark"
)}
title={title}
>
{title}
</span>
{description && (
<p
className={clsx(
"text-medium text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
"transition-all duration-200 ease-ease",
isSoon &&
"group-hover:text-medusa-fg-disabled dark:group-hover:text-medusa-fg-disabled-dark",
isHighlighted && "md:w-1/2"
)}
title={description}
>
{description}
</p>
)}
{html && (
<p
className={clsx(
"text-compact-medium text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
"transition-all duration-200 ease-ease",
isSoon &&
"group-hover:text-medusa-fg-disabled dark:group-hover:text-medusa-fg-disabled-dark",
isHighlighted && "md:w-1/2"
)}
dangerouslySetInnerHTML={{
__html: html,
}}
></p>
)}
</div>
</CardContainer>
)
}
function getCardIcon(item: ModifiedSidebarItem): JSX.Element {
if (item.customProps?.themedImage) {
return (
<BorderedIcon
icon={{
light: item.customProps.themedImage.light,
dark: item.customProps.themedImage.dark,
}}
iconWrapperClassName={clsx("p-[6px]")}
iconClassName={clsx("h-[20px] w-[20px]")}
/>
)
} else if (item.customProps?.image) {
return (
<BorderedIcon
icon={{
light: item.customProps.image,
}}
iconWrapperClassName={clsx("p-[6px]")}
iconClassName={clsx("h-[20px] w-[20px]")}
/>
)
} else if (item.customProps?.icon) {
return (
<BorderedIcon
IconComponent={item.customProps.icon}
iconWrapperClassName={clsx("p-[6px]")}
iconClassName={clsx("h-[20px] w-[20px]")}
/>
)
} else if (
item.customProps?.iconName &&
Object.hasOwn(Icons, item.customProps?.iconName)
) {
return (
<BorderedIcon
IconComponent={Icons[item.customProps?.iconName]}
iconWrapperClassName={clsx("p-[6px]")}
iconClassName={clsx("h-[20px] w-[20px]")}
/>
)
} else {
return (
<div
className={clsx(
// "card-icon-wrapper",
"no-zoom-img"
)}
>
{isInternalUrl(
"href" in item ? item.href : "value" in item ? item.value : "#"
)
? "📄️"
: "🔗"}
</div>
)
}
}
function CardCategory({
item,
}: {
item: ModifiedDocCardItemCategory
}): JSX.Element | null {
const href = findFirstCategoryLink(item)
const icon = getCardIcon(item)
// Unexpected: categories that don't have a link have been filtered upfront
if (!href) {
return null
}
return (
<CardLayout
{...item}
href={href}
icon={icon}
title={item.label}
// eslint-disable-next-line @docusaurus/string-literal-i18n-messages
description={translate(
{
message: item.customProps?.description || "{count} items",
id: "theme.docs.DocCard.categoryDescription",
description:
"The default description for a category card in the generated index about how many items this category includes",
},
{ count: item.items.length }
)}
containerClassName={item.customProps?.className}
isSoon={item.customProps?.isSoon}
badge={item.customProps?.badge}
/>
)
}
function CardLink({ item }: { item: ModifiedDocCardItemLink }): JSX.Element {
const icon = getCardIcon(item)
const doc = useDocById(item.docId ?? undefined)
return (
<CardLayout
{...item}
icon={icon}
title={item.label}
description={item.customProps?.description || doc?.description}
html={item.customProps?.html}
containerClassName={item.customProps?.className}
isSoon={item.customProps?.isSoon}
badge={item.customProps?.badge}
/>
)
}
export default function DocCard({ item }: ModifiedProps): JSX.Element {
switch (item.type) {
case "link":
return <CardLink item={item} />
case "category":
return <CardCategory item={item} />
default:
throw new Error(`unknown item type ${JSON.stringify(item)}`)
}
}

View File

@@ -0,0 +1,37 @@
import React from "react"
import clsx from "clsx"
import {
useCurrentSidebarCategory,
filterDocCardListItems,
} from "@docusaurus/theme-common"
import DocCard from "@theme/DocCard"
import type { Props } from "@theme/DocCardList"
function DocCardListForCurrentSidebarCategory({ className }: Props) {
const category = useCurrentSidebarCategory()
return <DocCardList items={category.items} className={className} />
}
type ModifiedProps = {
colSize?: string
} & Props
export default function DocCardList(props: ModifiedProps): JSX.Element {
const { items, className } = props
if (!items) {
return <DocCardListForCurrentSidebarCategory {...props} />
}
const filteredItems = filterDocCardListItems(items).filter(
(item) => !item.customProps.excludeFromDocList
)
return (
<section
className={clsx("cards-grid", `grid-${props.colSize || "4"}`, className)}
>
{filteredItems.map((item, index) => (
<DocCard item={item} key={index} />
))}
</section>
)
}

View File

@@ -0,0 +1,67 @@
import React from "react"
import clsx from "clsx"
import { ThemeClassNames } from "@docusaurus/theme-common"
import { useDoc } from "@docusaurus/theme-common/internal"
import Heading from "@theme/Heading"
import MDXContent from "@theme/MDXContent"
import type { Props } from "@theme/DocItem/Content"
import { DocContextValue } from "@medusajs/docs"
import { Badge, type BadgeVariant } from "docs-ui"
/**
Title can be declared inside md content or declared through
front matter and added manually. To make both cases consistent,
the added title is added under the same div.markdown block
See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
We render a "synthetic title" if:
- user doesn't ask to hide it with front matter
- the markdown content does not already contain a top-level h1 heading
*/
function useSyntheticTitle(): string | null {
const { metadata, frontMatter, contentTitle } = useDoc()
const shouldRender =
!frontMatter.hide_title && typeof contentTitle === "undefined"
if (!shouldRender) {
return null
}
return metadata.title
}
export default function DocItemContent({ children }: Props): JSX.Element {
const {
frontMatter: { badge },
} = useDoc() as DocContextValue
const syntheticTitle = useSyntheticTitle()
return (
<div className={clsx(ThemeClassNames.docs.docMarkdown, "markdown")}>
{syntheticTitle && (
<header
className={clsx(badge && "md:flex md:items-center md:gap-0.5 mb-2")}
>
<Heading as="h1" className={clsx(badge && "!mb-0")}>
{syntheticTitle}
{badge && (
<Badge
variant={badge.variant as BadgeVariant}
className="md:hidden ml-1 align-middle"
>
{badge.text}
</Badge>
)}
</Heading>
{badge && (
<Badge
variant={badge.variant as BadgeVariant}
className={clsx("md:block hidden")}
>
{badge.text}
</Badge>
)}
</header>
)}
<MDXContent>{children}</MDXContent>
</div>
)
}

View File

@@ -0,0 +1,29 @@
import React from "react"
import Footer from "@theme-original/DocItem/Footer"
import type FooterType from "@theme/DocItem/Footer"
import type { WrapperProps } from "@docusaurus/types"
import { useDoc } from "@docusaurus/theme-common/internal"
import { useThemeConfig } from "@docusaurus/theme-common"
import { ThemeConfig } from "@medusajs/docs"
import Feedback from "@site/src/components/Feedback"
type Props = WrapperProps<typeof FooterType>
export default function FooterWrapper(props: Props): JSX.Element {
const { metadata } = useDoc()
const { footerFeedback = { event: "" } } = useThemeConfig() as ThemeConfig
return (
<>
{!metadata.frontMatter?.hide_footer && (
<div className="mt-[42px]">
<Feedback
{...footerFeedback}
className="border-0 border-t border-solid border-medusa-border-base dark:border-medusa-border-base-dark"
/>
<Footer {...props} />
</div>
)}
</>
)
}

View File

@@ -0,0 +1,72 @@
import React from "react"
import clsx from "clsx"
import { useWindowSize } from "@docusaurus/theme-common"
import { useDoc } from "@docusaurus/theme-common/internal"
import DocItemPaginator from "@theme/DocItem/Paginator"
import DocVersionBanner from "@theme/DocVersionBanner"
import DocVersionBadge from "@theme/DocVersionBadge"
import DocItemFooter from "@theme/DocItem/Footer"
import DocItemTOCMobile from "@theme/DocItem/TOC/Mobile"
import DocItemTOCDesktop from "@theme/DocItem/TOC/Desktop"
import DocItemContent from "@theme/DocItem/Content"
import DocBreadcrumbs from "@theme/DocBreadcrumbs"
import type { Props } from "@theme/DocItem/Layout"
import Footer from "@theme/Footer"
import { useSidebar } from "../../../providers/Sidebar"
/**
* Decide if the toc should be rendered, on mobile or desktop viewports
*/
function useDocTOC() {
const { frontMatter, toc } = useDoc()
const windowSize = useWindowSize()
const hidden = frontMatter.hide_table_of_contents
const canRender = !hidden && toc.length > 0
const mobile = canRender ? <DocItemTOCMobile /> : undefined
const desktop =
canRender && (windowSize === "desktop" || windowSize === "ssr") ? (
<DocItemTOCDesktop />
) : undefined
return {
hidden,
mobile,
desktop,
}
}
export default function DocItemLayout({ children }: Props): JSX.Element {
const docTOC = useDocTOC()
const sidebarContext = useSidebar()
return (
<div className="row m-0">
<div
className={clsx(
"col",
"my-0 mx-auto max-w-main-content w-full ml-auto lg:py-0 py-0 px-1",
!docTOC.hidden && "w-9/12",
!sidebarContext?.hiddenSidebarContainer && "!max-w-[720px]"
)}
>
<DocVersionBanner />
<div>
<article className={clsx("[&>*:first-child]:mt-0")}>
<DocBreadcrumbs />
<DocVersionBadge />
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
<DocItemFooter />
</article>
<DocItemPaginator />
<Footer />
</div>
</div>
{docTOC.desktop && (
<div className="col toc-wrapper">{docTOC.desktop}</div>
)}
</div>
)
}

View File

@@ -0,0 +1,31 @@
import React from "react"
import clsx from "clsx"
import { useDocsSidebar } from "@docusaurus/theme-common/internal"
import type { Props } from "@theme/DocPage/Layout/Main"
import { useSidebar } from "@site/src/providers/Sidebar"
export default function DocPageLayoutMain({ children }: Props): JSX.Element {
const sidebar = useDocsSidebar()
const sidebarContext = useSidebar()
return (
<main
className={clsx(
"flex flex-col w-full lg:flex-grow",
(sidebarContext?.hiddenSidebarContainer || !sidebar) &&
"lg:max-w-[calc(100%-30px)]",
!sidebarContext?.hiddenSidebarContainer &&
"xxl:max-w-[1119px] lg:max-w-[calc(100%-321px)]"
)}
>
<div
className={clsx(
"container padding-top--md px-0",
sidebarContext?.hiddenSidebarContainer &&
"lg:max-w-main-content-hidden-sidebar"
)}
>
{children}
</div>
</main>
)
}

View File

@@ -0,0 +1,105 @@
import React, { type ReactNode, useRef } from "react"
import clsx from "clsx"
import { ThemeClassNames } from "@docusaurus/theme-common"
import { useDocsSidebar } from "@docusaurus/theme-common/internal"
import { useLocation } from "@docusaurus/router"
import DocSidebar from "@theme/DocSidebar"
import type { Props } from "@theme/DocPage/Layout/Sidebar"
import { SwitchTransition, CSSTransition } from "react-transition-group"
import { useSidebar } from "@site/src/providers/Sidebar"
// Reset sidebar state when sidebar changes
// Use React key to unmount/remount the children
// See https://github.com/facebook/docusaurus/issues/3414
function ResetOnSidebarChange({ children }: { children: ReactNode }) {
const sidebar = useDocsSidebar()
return (
<React.Fragment key={sidebar?.name ?? "noSidebar"}>
{children}
</React.Fragment>
)
}
export default function DocPageLayoutSidebar({
sidebar,
hiddenSidebarContainer,
}: Props): JSX.Element {
const { pathname } = useLocation()
const sidebarContext = useSidebar()
const { name } = useDocsSidebar()
const sidebarRef = useRef(null)
return (
<aside
className={clsx(
ThemeClassNames.docs.docSidebarContainer,
"min-[997px]:block min-[997px]:w-sidebar min-[997px]:transition-[left] min-[997px]:ease-ease min-[997px]:duration-200 min-[997px]:left-0 hidden",
!hiddenSidebarContainer && "clip",
hiddenSidebarContainer &&
"min-[997px]:fixed min-[997px]:left-[-100%] min-[997px]:rounded min-[997px]:border-0 min-[997px]:border-medusa-border-strong min-[997px]:dark:border-medusa-border-strong-dark",
hiddenSidebarContainer &&
sidebarContext?.floatingSidebar &&
"min-[997px]:!left-0.5 min-[997px]:top-[65px] min-[997px]:z-20 min-[997px]:bg-docs-bg min-[997px]:dark:bg-docs-bg-dark min-[997px]:shadow-flyout min-[997px]:dark:shadow-flyout-dark"
)}
onTransitionEnd={(e) => {
if (
!e.currentTarget.classList.contains(
ThemeClassNames.docs.docSidebarContainer
)
) {
return
}
if (hiddenSidebarContainer) {
sidebarContext?.setHiddenSidebar(true)
}
}}
onMouseLeave={() => {
setTimeout(() => {
if (!document.querySelector(".sidebar-toggler:hover")) {
sidebarContext?.setFloatingSidebar(false)
}
}, 100)
}}
onMouseUp={(e) => {
const target = e.target as Element
if (
target.classList.contains("menu__list-item") ||
target.parentElement.classList.contains("menu__list-item")
) {
sidebarContext?.setFloatingSidebar(false)
}
}}
>
<SwitchTransition>
<CSSTransition
key={name}
nodeRef={sidebarRef}
classNames={{
enter: "animate__animated animate__fadeInLeft animate__fastest",
exit: "animate__animated animate__fadeOutLeft animate__fastest",
}}
timeout={200}
>
<div
className={clsx(
"min-[997px]:top-[57px] min-[997px]:sticky min-[997px]:max-h-screen min-[997px]:[&>div]:max-h-screen"
)}
ref={sidebarRef}
>
<ResetOnSidebarChange>
<div>
<DocSidebar
sidebar={sidebar}
path={pathname}
onCollapse={sidebarContext?.onCollapse}
isHidden={sidebarContext?.hiddenSidebar}
/>
</div>
</ResetOnSidebarChange>
</div>
</CSSTransition>
</SwitchTransition>
</aside>
)
}

View File

@@ -0,0 +1,40 @@
import React from "react"
import { useDocsSidebar } from "@docusaurus/theme-common/internal"
import Layout from "@theme/Layout"
import BackToTopButton from "@theme/BackToTopButton"
import DocPageLayoutSidebar from "@theme/DocPage/Layout/Sidebar"
import DocPageLayoutMain from "@theme/DocPage/Layout/Main"
import type { Props } from "@theme/DocPage/Layout"
import clsx from "clsx"
import { useSidebar } from "../../../providers/Sidebar"
import useOnboarding from "../../../hooks/use-onboarding"
import useCurrentLearningPath from "../../../hooks/use-current-learning-path"
export default function DocPageLayout({ children }: Props): JSX.Element {
const sidebar = useDocsSidebar()
const sidebarContext = useSidebar()
useOnboarding()
useCurrentLearningPath()
return (
<Layout wrapperClassName={clsx("flex flex-[1_0_auto]")}>
<BackToTopButton />
<div className={clsx("flex w-full flex-[1_0]")}>
{sidebar && (
<DocPageLayoutSidebar
sidebar={sidebar.items}
hiddenSidebarContainer={sidebarContext?.hiddenSidebarContainer}
setHiddenSidebarContainer={
sidebarContext?.setHiddenSidebarContainer
}
/>
)}
<DocPageLayoutMain
hiddenSidebarContainer={sidebarContext?.hiddenSidebarContainer}
>
{children}
</DocPageLayoutMain>
</div>
</Layout>
)
}

View File

@@ -0,0 +1,73 @@
import React from "react"
import clsx from "clsx"
import {
HtmlClassNameProvider,
ThemeClassNames,
PageMetadata,
} from "@docusaurus/theme-common"
import {
docVersionSearchTag,
DocsSidebarProvider,
DocsVersionProvider,
useDocRouteMetadata,
} from "@docusaurus/theme-common/internal"
import DocPageLayout from "@theme/DocPage/Layout"
import NotFound from "@theme/NotFound"
import SearchMetadata from "@theme/SearchMetadata"
import type { Props } from "@theme/DocPage"
import SidebarProvider from "@site/src/providers/Sidebar"
import DocsProviders from "../../providers/DocsProviders"
function DocPageMetadata(props: Props): JSX.Element {
const { versionMetadata } = props
return (
<>
<SearchMetadata
version={versionMetadata.version}
tag={docVersionSearchTag(
versionMetadata.pluginId,
versionMetadata.version
)}
/>
<PageMetadata>
{versionMetadata.noIndex && (
<meta name="robots" content="noindex, nofollow" />
)}
</PageMetadata>
</>
)
}
export default function DocPage(props: Props): JSX.Element {
const { versionMetadata } = props
const currentDocRouteMetadata = useDocRouteMetadata(props)
if (!currentDocRouteMetadata) {
return <NotFound />
}
const { docElement, sidebarName, sidebarItems } = currentDocRouteMetadata
return (
<>
<DocPageMetadata {...props} />
<HtmlClassNameProvider
className={clsx(
// TODO: it should be removed from here
ThemeClassNames.wrapper.docsPages,
ThemeClassNames.page.docsDocPage,
props.versionMetadata.className
// sidebarName && "doc-has-sidebar"
)}
>
<DocsVersionProvider version={versionMetadata}>
<DocsProviders>
<DocsSidebarProvider name={sidebarName} items={sidebarItems}>
<SidebarProvider sidebarName={sidebarName}>
<DocPageLayout>{docElement}</DocPageLayout>
</SidebarProvider>
</DocsSidebarProvider>
</DocsProviders>
</DocsVersionProvider>
</HtmlClassNameProvider>
</>
)
}

View File

@@ -0,0 +1,95 @@
import React, { useEffect, useRef } from "react"
import clsx from "clsx"
import { useThemeConfig } from "@docusaurus/theme-common"
import Content from "@theme/DocSidebar/Desktop/Content"
import type { Props } from "@theme/DocSidebar/Desktop"
import useIsBrowser from "@docusaurus/useIsBrowser"
import { useLocation } from "@docusaurus/router"
import AnnouncementBar from "../../AnnouncementBar/index"
function DocSidebarDesktop({ path, sidebar }: Props) {
const {
navbar: { hideOnScroll },
} = useThemeConfig()
const isBrowser = useIsBrowser()
const sidebarRef = useRef(null)
const location = useLocation()
useEffect(() => {
function handleScroll() {
if (!sidebarRef.current?.classList.contains("scrolling")) {
sidebarRef.current?.classList.add("scrolling")
const intervalId = setInterval(() => {
if (!sidebarRef.current?.matches(":hover")) {
sidebarRef.current?.classList.remove("scrolling")
clearInterval(intervalId)
}
}, 300)
}
}
if (isBrowser && sidebarRef.current) {
const navElement = sidebarRef.current.querySelector(".main-sidebar")
navElement.addEventListener("scroll", handleScroll)
return () => {
navElement?.removeEventListener("scroll", handleScroll)
}
}
}, [isBrowser, sidebarRef.current])
useEffect(() => {
const navElement = sidebarRef.current.querySelector(".main-sidebar")
const navElementBoundingRect = navElement.getBoundingClientRect()
// logic to scroll to current active item
const activeItem = document.querySelector(
".sidebar-desktop [aria-current=page]"
)
if (!activeItem) {
return
}
const activeItemBoundingReact = activeItem.getBoundingClientRect()
// the extra 160 is due to the sticky elements in the sidebar
const isActiveItemVisible =
activeItemBoundingReact.top >= 0 &&
activeItemBoundingReact.bottom + 160 <= navElementBoundingRect.height
if (!isActiveItemVisible) {
const elementToScrollTo = activeItem
const elementBounding = elementToScrollTo.getBoundingClientRect()
// scroll to element
navElement.scroll({
// the extra 160 is due to the sticky elements in the sidebar
top:
elementBounding.top -
navElementBoundingRect.top +
navElement.scrollTop -
160,
behaviour: "smooth",
})
}
}, [location])
return (
<div
className={clsx(
"lg:flex lg:flex-col lg:max-h-screen lg:h-full lg:sticky lg:top-0 lg:transition-opacity lg:duration-[50ms] lg:ease-ease lg:pt-1.5",
"sidebar-desktop",
hideOnScroll && "lg:pt-0"
)}
ref={sidebarRef}
>
<AnnouncementBar />
<Content
path={path}
sidebar={sidebar}
className={clsx("main-sidebar", "!mt-0 !pt-0 !px-1.5 !pb-4")}
/>
</div>
)
}
export default React.memo(DocSidebarDesktop)

View File

@@ -0,0 +1,238 @@
import React, { type ComponentProps, useEffect, useMemo } from "react"
import clsx from "clsx"
import {
ThemeClassNames,
useThemeConfig,
usePrevious,
Collapsible,
useCollapsible,
} from "@docusaurus/theme-common"
import {
isActiveSidebarItem,
findFirstCategoryLink,
useDocSidebarItemsExpandedState,
isSamePath,
} from "@docusaurus/theme-common/internal"
import Link from "@docusaurus/Link"
import { translate } from "@docusaurus/Translate"
import useIsBrowser from "@docusaurus/useIsBrowser"
import DocSidebarItems from "@theme/DocSidebarItems"
import type { Props } from "@theme/DocSidebarItem/Category"
import { ModifiedPropSidebarItemCategory } from "@medusajs/docs"
import DocSidebarItemIcon from "../../../components/DocSidebarItemIcon"
import { Badge } from "docs-ui"
type ModifiedProps = Props & {
item: ModifiedPropSidebarItemCategory
}
// If we navigate to a category and it becomes active, it should automatically
// expand itself
function useAutoExpandActiveCategory({
isActive,
collapsed,
updateCollapsed,
}: {
isActive: boolean
collapsed: boolean
updateCollapsed: (b: boolean) => void
}) {
const wasActive = usePrevious(isActive)
useEffect(() => {
const justBecameActive = isActive && !wasActive
if (justBecameActive && collapsed) {
updateCollapsed(false)
}
}, [isActive, wasActive, collapsed, updateCollapsed])
}
/**
* When a collapsible category has no link, we still link it to its first child
* during SSR as a temporary fallback. This allows to be able to navigate inside
* the category even when JS fails to load, is delayed or simply disabled
* React hydration becomes an optional progressive enhancement
* see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188
* see https://github.com/facebook/docusaurus/issues/3030
*/
function useCategoryHrefWithSSRFallback(
item: Props["item"]
): string | undefined {
const isBrowser = useIsBrowser()
return useMemo(() => {
if (item.href) {
return item.href
}
// In these cases, it's not necessary to render a fallback
// We skip the "findFirstCategoryLink" computation
if (isBrowser || !item.collapsible) {
return undefined
}
return findFirstCategoryLink(item)
}, [item, isBrowser])
}
function CollapseButton({
categoryLabel,
onClick,
}: {
categoryLabel: string
onClick: ComponentProps<"button">["onClick"]
}) {
return (
<button
aria-label={translate(
{
id: "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",
message: "Toggle the collapsible sidebar category '{label}'",
description:
"The ARIA label to toggle the collapsible sidebar category",
},
{ label: categoryLabel }
)}
type="button"
className="hidden"
onClick={onClick}
/>
)
}
export default function DocSidebarItemCategory({
item,
onItemClick,
activePath,
level,
index,
...props
}: ModifiedProps): JSX.Element {
const { items, label, collapsible, className, href, customProps } = item
const {
docs: {
sidebar: { autoCollapseCategories },
},
} = useThemeConfig()
const hrefWithSSRFallback = useCategoryHrefWithSSRFallback(item)
const isActive = isActiveSidebarItem(item, activePath)
const isCurrentPage = isSamePath(href, activePath)
const { collapsed, setCollapsed } = useCollapsible({
// Active categories are always initialized as expanded. The default
// (`item.collapsed`) is only used for non-active categories.
initialState: () => {
if (!collapsible) {
return false
}
return isActive ? false : item.collapsed
},
})
const { expandedItem, setExpandedItem } = useDocSidebarItemsExpandedState()
// Use this instead of `setCollapsed`, because it is also reactive
const updateCollapsed = (toCollapsed = !collapsed) => {
setExpandedItem(toCollapsed ? null : index)
setCollapsed(toCollapsed)
}
useAutoExpandActiveCategory({ isActive, collapsed, updateCollapsed })
useEffect(() => {
if (
collapsible &&
expandedItem != null &&
expandedItem !== index &&
autoCollapseCategories
) {
setCollapsed(true)
}
}, [collapsible, expandedItem, index, setCollapsed, autoCollapseCategories])
return (
<li
className={clsx(
ThemeClassNames.docs.docSidebarItemCategory,
ThemeClassNames.docs.docSidebarItemCategoryLevel(level),
"menu__list-item",
// {
// "menu__list-item--collapsed": collapsed,
// },
className,
customProps?.sidebar_is_title && "sidebar-title",
customProps?.sidebar_is_group_headline && "sidebar-group-headline",
customProps?.sidebar_is_group_divider && "sidebar-group-divider",
customProps?.sidebar_is_divider_line && "sidebar-divider-line",
customProps?.sidebar_is_back_link && "sidebar-back-link",
customProps?.sidebar_is_soon &&
"sidebar-soon-link sidebar-badge-wrapper",
!customProps?.sidebar_is_title &&
"[&_.sidebar-item-icon]:w-[20px] [&_.sidebar-item-icon]:h-[20px]",
!customProps?.sidebar_is_title &&
!customProps?.sidebar_is_back_link &&
"[&_.sidebar-item-icon]:mr-0.75"
)}
>
<div
className={clsx("menu__list-item-collapsible", {
"menu__list-item-collapsible--active": isCurrentPage,
})}
>
<Link
className={clsx("menu__link", {
"menu__link--sublist": collapsible,
"menu__link--sublist-caret": !href && collapsible,
"menu__link--active": isActive,
})}
onClick={
collapsible
? (e) => {
onItemClick?.(item)
if (href) {
updateCollapsed(false)
} else {
e.preventDefault()
updateCollapsed()
}
}
: () => {
onItemClick?.(item)
}
}
aria-current={isCurrentPage ? "page" : undefined}
aria-expanded={collapsible ? !collapsed : undefined}
href={collapsible ? hrefWithSSRFallback ?? "#" : hrefWithSSRFallback}
{...props}
>
{customProps?.sidebar_icon && (
<DocSidebarItemIcon
icon={customProps.sidebar_icon}
is_title={customProps.sidebar_is_title}
is_disabled={customProps?.sidebar_is_soon}
/>
)}
{label}
</Link>
{href && collapsible && (
<CollapseButton
categoryLabel={label}
onClick={(e) => {
e.preventDefault()
updateCollapsed()
}}
/>
)}
{customProps?.sidebar_is_soon && (
<Badge variant="purple" className={`sidebar-soon-badge`}>
Soon
</Badge>
)}
</div>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
<DocSidebarItems
items={items}
tabIndex={collapsed ? -1 : 0}
onItemClick={onItemClick}
activePath={activePath}
level={level + 1}
/>
</Collapsible>
</li>
)
}

View File

@@ -0,0 +1,60 @@
import React from "react"
import clsx from "clsx"
import { ThemeClassNames } from "@docusaurus/theme-common"
import type { Props } from "@theme/DocSidebarItem/Html"
import { ModifiedPropSidebarItemHtml } from "@medusajs/docs"
import DocSidebarItemIcon from "../../../components/DocSidebarItemIcon"
import { Badge } from "docs-ui"
type ModifiedProps = Props & {
item: ModifiedPropSidebarItemHtml
}
export default function DocSidebarItemHtml({
item,
level,
index,
}: ModifiedProps): JSX.Element {
const { value, defaultStyle, className, customProps } = item
return (
<li
className={clsx(
ThemeClassNames.docs.docSidebarItemLink,
ThemeClassNames.docs.docSidebarItemLinkLevel(level),
defaultStyle && ["lg:py-[6px] lg:px-1", "menu__list-item"],
className,
customProps?.sidebar_is_title && "sidebar-title",
customProps?.sidebar_is_group_headline && "sidebar-group-headline",
customProps?.sidebar_is_group_divider && "sidebar-group-divider",
customProps?.sidebar_is_divider_line && "sidebar-divider-line",
customProps?.sidebar_is_back_link && "sidebar-back-link",
customProps?.sidebar_is_soon &&
"sidebar-soon-link sidebar-badge-wrapper",
!customProps?.sidebar_is_title &&
"[&_.sidebar-item-icon]:w-[20px] [&_.sidebar-item-icon]:h-[20px]",
!customProps?.sidebar_is_title &&
!customProps?.sidebar_is_back_link &&
"[&_.sidebar-item-icon]:mr-0.75"
)}
key={index}
>
{customProps?.sidebar_icon && (
<DocSidebarItemIcon
icon={customProps.sidebar_icon}
is_title={customProps.sidebar_is_title}
is_disabled={customProps?.sidebar_is_soon}
/>
)}
<span
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: value }}
></span>
{customProps?.sidebar_is_soon && (
<Badge variant="purple" className={`sidebar-soon-badge`}>
Soon
</Badge>
)}
</li>
)
}

View File

@@ -0,0 +1,78 @@
import React from "react"
import clsx from "clsx"
import { ThemeClassNames } from "@docusaurus/theme-common"
import { isActiveSidebarItem } from "@docusaurus/theme-common/internal"
import Link from "@docusaurus/Link"
import isInternalUrl from "@docusaurus/isInternalUrl"
import IconExternalLink from "@theme/Icon/ExternalLink"
import type { Props } from "@theme/DocSidebarItem/Link"
import { ModifiedPropSidebarItemLink } from "@medusajs/docs"
import DocSidebarItemIcon from "../../../components/DocSidebarItemIcon"
import { Badge } from "docs-ui"
type ModifiedProps = Props & {
item: ModifiedPropSidebarItemLink
}
export default function DocSidebarItemLink({
item,
onItemClick,
activePath,
level,
...props
}: ModifiedProps): JSX.Element {
const { href, label, className, autoAddBaseUrl, customProps } = item
const isActive = isActiveSidebarItem(item, activePath)
const isInternalLink = isInternalUrl(href)
return (
<li
className={clsx(
ThemeClassNames.docs.docSidebarItemLink,
ThemeClassNames.docs.docSidebarItemLinkLevel(level),
"menu__list-item",
className,
customProps?.sidebar_is_title && "sidebar-title",
customProps?.sidebar_is_group_headline && "sidebar-group-headline",
customProps?.sidebar_is_group_divider && "sidebar-group-divider",
customProps?.sidebar_is_divider_line && "sidebar-divider-line",
customProps?.sidebar_is_back_link && "sidebar-back-link",
customProps?.sidebar_is_soon &&
"sidebar-soon-link sidebar-badge-wrapper",
!customProps?.sidebar_is_title &&
"[&_.sidebar-item-icon]:w-[20px] [&_.sidebar-item-icon]:h-[20px]",
!customProps?.sidebar_is_title &&
!customProps?.sidebar_is_back_link &&
"[&_.sidebar-item-icon]:mr-0.75"
)}
key={label}
>
<Link
className={clsx("menu__link", !isInternalLink && "items-center", {
"menu__link--active": isActive,
})}
autoAddBaseUrl={autoAddBaseUrl}
aria-current={isActive ? "page" : undefined}
to={href}
{...(isInternalLink && {
onClick: onItemClick ? () => onItemClick(item) : undefined,
})}
{...props}
>
{customProps?.sidebar_icon && (
<DocSidebarItemIcon
icon={customProps.sidebar_icon}
is_title={customProps.sidebar_is_title}
is_disabled={customProps?.sidebar_is_soon}
/>
)}
{label}
{!isInternalLink && <IconExternalLink />}
</Link>
{customProps?.sidebar_is_soon && (
<Badge variant="purple" className={`sidebar-soon-badge`}>
Soon
</Badge>
)}
</li>
)
}

View File

@@ -0,0 +1,26 @@
import React from "react"
import Translate from "@docusaurus/Translate"
import { ThemeClassNames } from "@docusaurus/theme-common"
import type { Props } from "@theme/EditThisPage"
import clsx from "clsx"
import { Button } from "docs-ui"
export default function EditThisPage({ editUrl }: Props): JSX.Element {
return (
<Button variant="secondary">
<a
href={editUrl}
target="_blank"
rel="noreferrer noopener"
className={clsx(ThemeClassNames.common.editThisPage)}
>
<Translate
id="theme.common.editThisPage"
description="The link label to edit the current page"
>
Edit this page
</Translate>
</a>
</Button>
)
}

View File

@@ -0,0 +1,51 @@
import React from "react"
import clsx from "clsx"
import type { Props } from "@theme/Footer/Layout"
import { useThemeConfig } from "@docusaurus/theme-common"
import { ThemeConfig } from "@medusajs/docs"
import SocialLinks from "@site/src/components/Footer/SocialLinks"
export default function FooterLayout({
style,
links,
logo,
copyright,
}: Props): JSX.Element {
const { socialLinks } = useThemeConfig() as ThemeConfig
return (
<footer
className={clsx(
"footer",
"border-t border-x-0 border-b-0 border-solid border-medusa-border-base dark:border-medusa-border-base-dark",
"pt-[108px] pb-4 mt-2",
{
"footer--dark": style === "dark",
}
)}
>
<div
className={clsx(
"container container-fluid",
"flex !px-0",
"[&_.col]:!px-0",
"lg:flex-row flex-col",
"!pt-0"
)}
>
{(logo || copyright || socialLinks) && (
<div className="col col--6">
<div className={clsx("lg:mb-0 mb-2")}>
{logo && <div>{logo}</div>}
{copyright}
</div>
</div>
)}
<div className={clsx("col col--6 row lg:justify-end justify-start")}>
{socialLinks && <SocialLinks links={socialLinks} />}
{links}
</div>
</div>
</footer>
)
}

View File

@@ -0,0 +1,26 @@
import React from "react"
import { useThemeConfig } from "@docusaurus/theme-common"
import FooterLinks from "@theme/Footer/Links"
import FooterLogo from "@theme/Footer/Logo"
import FooterCopyright from "@theme/Footer/Copyright"
import FooterLayout from "@theme/Footer/Layout"
function Footer(): JSX.Element | null {
const { footer } = useThemeConfig()
if (!footer) {
return null
}
const { copyright, links, logo, style } = footer
return (
<FooterLayout
style={style}
links={links && links.length > 0 && <FooterLinks links={links} />}
logo={logo && <FooterLogo logo={logo} />}
copyright={copyright && <FooterCopyright copyright={copyright} />}
/>
)
}
export default React.memo(Footer)

View File

@@ -0,0 +1,62 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconCircleDottedLine = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M12.5 2.93589C10.884 2.3547 9.116 2.3547 7.5 2.93589"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M2.63049 8.63198C2.94295 6.94471 3.82573 5.41606 5.13094 4.30209"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.13094 15.6979C3.82575 14.5839 2.94298 13.0552 2.63049 11.368"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.5 17.0641C9.116 17.6453 10.884 17.6453 12.5 17.0641"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M17.3695 8.63198C17.057 6.94471 16.1742 5.41606 14.869 4.30209"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M14.869 15.6979C16.1742 14.5839 17.057 13.0552 17.3695 11.368"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
export default IconCircleDottedLine

View File

@@ -0,0 +1,9 @@
import React from "react"
import { XMark } from "@medusajs/icons"
import { IconProps } from "@medusajs/icons/dist/types"
const IconClose = (props: IconProps) => {
return <XMark {...props} />
}
export default IconClose

View File

@@ -0,0 +1,9 @@
import React from "react"
import { SquareTwoStackSolid } from "@medusajs/icons"
import { IconProps } from "@medusajs/icons/dist/types"
const IconCopy = (props: IconProps) => {
return <SquareTwoStackSolid {...props} />
}
export default IconCopy

View File

@@ -0,0 +1,9 @@
import { Moon } from "@medusajs/icons"
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
const IconDarkMode = (props: IconProps) => {
return <Moon {...props} />
}
export default IconDarkMode

View File

@@ -0,0 +1,24 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconDiscord = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M12.9953 12.4964C12.1086 12.4964 11.3775 11.6826 11.3775 10.6825C11.3775 9.68252 12.0938 8.86871 12.9953 8.86871C13.8968 8.86871 14.6268 9.6904 14.613 10.6825C14.5992 11.6747 13.9037 12.4964 12.9953 12.4964ZM7.01487 12.4964C6.12815 12.4964 5.3971 11.6826 5.3971 10.6825C5.3971 9.68252 6.11337 8.86871 7.01487 8.86871C7.91636 8.86871 8.64642 9.6904 8.63263 10.6825C8.61884 11.6747 7.91538 12.4964 7.01487 12.4964ZM16.2357 4.2765C15.069 3.741 13.8376 3.35915 12.5726 3.14052C12.5611 3.13824 12.5492 3.13968 12.5385 3.14464C12.5279 3.1496 12.5191 3.15782 12.5135 3.16811C12.3458 3.47256 12.1935 3.78528 12.0573 4.10507C10.6937 3.89797 9.3066 3.89797 7.94296 4.10507C7.80595 3.78447 7.65136 3.47168 7.4799 3.16811C7.47394 3.15812 7.46512 3.15016 7.45458 3.14524C7.44404 3.14032 7.43227 3.13868 7.42078 3.14052C6.15564 3.35869 4.92426 3.74056 3.75767 4.2765C3.74785 4.28081 3.73959 4.28803 3.73402 4.29719C1.40097 7.78297 0.761549 11.183 1.07387 14.5437C1.07476 14.552 1.07732 14.56 1.08138 14.5673C1.08545 14.5746 1.09093 14.5809 1.09752 14.5861C2.45592 15.5924 3.97543 16.3607 5.59119 16.858C5.60256 16.8614 5.61469 16.8613 5.62596 16.8576C5.63724 16.8539 5.64711 16.8468 5.65425 16.8373C6.00123 16.3649 6.30868 15.8647 6.57348 15.3417C6.57701 15.3346 6.57899 15.3267 6.57929 15.3187C6.5796 15.3107 6.57822 15.3028 6.57525 15.2953C6.57228 15.2879 6.56778 15.2812 6.56204 15.2756C6.55631 15.27 6.54946 15.2657 6.54195 15.2629C6.05702 15.0774 5.58757 14.8537 5.13798 14.5939C5.1293 14.5892 5.12196 14.5824 5.11666 14.5741C5.11135 14.5657 5.10826 14.5562 5.10766 14.5463C5.10707 14.5365 5.109 14.5266 5.11328 14.5177C5.11755 14.5088 5.12401 14.5011 5.13207 14.4954C5.2306 14.4245 5.32124 14.3516 5.4109 14.2767C5.41886 14.27 5.42854 14.2658 5.43883 14.2644C5.44911 14.263 5.45958 14.2645 5.46903 14.2688C8.41391 15.6137 11.6031 15.6137 14.5135 14.2688C14.523 14.2642 14.5336 14.2624 14.5441 14.2636C14.5546 14.2648 14.5645 14.269 14.5726 14.2757C14.6623 14.3496 14.7569 14.4245 14.8524 14.4944C14.8605 14.5001 14.8671 14.5077 14.8714 14.5165C14.8758 14.5254 14.8778 14.5352 14.8773 14.5451C14.8768 14.5549 14.8738 14.5645 14.8686 14.5729C14.8634 14.5812 14.8561 14.5882 14.8475 14.593C14.4006 14.8535 13.9327 15.0759 13.4485 15.258C13.4409 15.2608 13.4341 15.2653 13.4283 15.271C13.4226 15.2767 13.4182 15.2835 13.4153 15.291C13.4124 15.2986 13.4111 15.3066 13.4116 15.3147C13.4121 15.3228 13.4142 15.3306 13.4179 15.3378C13.6867 15.8581 13.9933 16.358 14.3352 16.8334C14.3421 16.8432 14.3519 16.8505 14.3632 16.8544C14.3745 16.8583 14.3868 16.8585 14.3983 16.8551C16.0168 16.3593 17.5388 15.591 18.8988 14.5831C18.9055 14.5783 18.911 14.5721 18.9151 14.565C18.9192 14.5578 18.9217 14.5499 18.9225 14.5417C19.2978 10.6599 18.2939 7.2874 16.2623 4.29522C16.2563 4.28616 16.2462 4.27953 16.2357 4.2765Z"
fill="currentColor"
/>
</svg>
)
}
export default IconDiscord

View File

@@ -0,0 +1,9 @@
import { ArrowUpRightOnBox } from "@medusajs/icons"
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
const IconExternalLink = (props: IconProps) => {
return <ArrowUpRightOnBox {...props} />
}
export default IconExternalLink

View File

@@ -0,0 +1,26 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconGitHub = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 1.22205C5.0275 1.22205 1 5.24955 1 10.222C1 14.2045 3.57625 17.5683 7.15375 18.7608C7.60375 18.8395 7.7725 18.5695 7.7725 18.3333C7.7725 18.1195 7.76125 17.4108 7.76125 16.657C5.5 17.0733 4.915 16.1058 4.735 15.5995C4.63375 15.3408 4.195 14.542 3.8125 14.3283C3.4975 14.1595 3.0475 13.7433 3.80125 13.732C4.51 13.7208 5.01625 14.3845 5.185 14.6545C5.995 16.0158 7.28875 15.6333 7.80625 15.397C7.885 14.812 8.12125 14.4183 8.38 14.1933C6.3775 13.9683 4.285 13.192 4.285 9.74955C4.285 8.7708 4.63375 7.9608 5.2075 7.3308C5.1175 7.1058 4.8025 6.1833 5.2975 4.9458C5.2975 4.9458 6.05125 4.70955 7.7725 5.8683C8.4925 5.6658 9.2575 5.56455 10.0225 5.56455C10.7875 5.56455 11.5525 5.6658 12.2725 5.8683C13.9938 4.6983 14.7475 4.9458 14.7475 4.9458C15.2425 6.1833 14.9275 7.1058 14.8375 7.3308C15.4113 7.9608 15.76 8.75955 15.76 9.74955C15.76 13.2033 13.6562 13.9683 11.6538 14.1933C11.98 14.4745 12.2613 15.0145 12.2613 15.8583C12.2613 17.062 12.25 18.0295 12.25 18.3333C12.25 18.5695 12.4188 18.8508 12.8688 18.7608C16.4238 17.5683 19 14.1933 19 10.222C19 5.24955 14.9725 1.22205 10 1.22205Z"
fill="currentColor"
/>
</svg>
)
}
export default IconGitHub

View File

@@ -0,0 +1,9 @@
import React from "react"
import { Sun } from "@medusajs/icons"
import { IconProps } from "@medusajs/icons/dist/types"
const IconLightMode = (props: IconProps) => {
return <Sun {...props} />
}
export default IconLightMode

View File

@@ -0,0 +1,24 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconLinkedIn = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M15.25 1H4.75C2.67925 1 1 2.67925 1 4.75V15.25C1 17.3208 2.67925 19 4.75 19H15.25C17.3215 19 19 17.3208 19 15.25V4.75C19 2.67925 17.3215 1 15.25 1ZM7 15.25H4.75V7H7V15.25ZM5.875 6.049C5.1505 6.049 4.5625 5.4565 4.5625 4.726C4.5625 3.9955 5.1505 3.403 5.875 3.403C6.5995 3.403 7.1875 3.9955 7.1875 4.726C7.1875 5.4565 6.60025 6.049 5.875 6.049ZM16 15.25H13.75V11.047C13.75 8.521 10.75 8.71225 10.75 11.047V15.25H8.5V7H10.75V8.32375C11.797 6.38425 16 6.241 16 10.1808V15.25Z"
fill="currentColor"
/>
</svg>
)
}
export default IconLinkedIn

View File

@@ -0,0 +1,28 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconNextjs = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M9.41129 1.01314C9.37262 1.01666 9.24959 1.02896 9.13885 1.03775C6.585 1.268 4.19285 2.64599 2.67777 4.76395C1.8341 5.94157 1.2945 7.27737 1.09062 8.69227C1.01855 9.18617 1.00977 9.33206 1.00977 10.0017C1.00977 10.6714 1.01855 10.8173 1.09062 11.3112C1.57924 14.6876 3.98194 17.5244 7.2406 18.5755C7.82414 18.7636 8.43931 18.8919 9.13885 18.9692C9.41129 18.9991 10.5889 18.9991 10.8613 18.9692C12.0688 18.8356 13.0918 18.5368 14.1007 18.0218C14.2553 17.9427 14.2852 17.9216 14.2641 17.9041C14.2501 17.8935 13.591 17.0094 12.8 15.9408L11.3623 13.9986L9.56069 11.3323C8.56938 9.86638 7.75383 8.66767 7.7468 8.66767C7.73977 8.66591 7.73274 9.85056 7.72923 11.2971C7.72395 13.8299 7.7222 13.9318 7.69056 13.9916C7.64486 14.0777 7.60971 14.1128 7.53589 14.1515C7.47964 14.1796 7.43043 14.1849 7.16502 14.1849H6.86095L6.7801 14.1339C6.72737 14.1005 6.6887 14.0566 6.66234 14.0056L6.62543 13.9265L6.62894 10.4025L6.63422 6.87663L6.6887 6.80808C6.71683 6.77117 6.77659 6.72372 6.81877 6.70087C6.89083 6.66571 6.91895 6.6622 7.22303 6.6622C7.58158 6.6622 7.64134 6.67626 7.7345 6.7782C7.76086 6.80632 8.73635 8.27571 9.90343 10.0457C11.0705 11.8156 12.6664 14.2324 13.4503 15.4188L14.874 17.5754L14.9461 17.5279C15.5841 17.1131 16.2591 16.5226 16.7934 15.9074C17.9306 14.6015 18.6635 13.009 18.9096 11.3112C18.9816 10.8173 18.9904 10.6714 18.9904 10.0017C18.9904 9.33206 18.9816 9.18617 18.9096 8.69227C18.421 5.31585 16.0183 2.47901 12.7596 1.42794C12.1848 1.24163 11.5732 1.11333 10.8877 1.03599C10.719 1.01841 9.55717 0.999079 9.41129 1.01314ZM13.0918 6.45128C13.1762 6.49346 13.2447 6.57432 13.2693 6.65868C13.2834 6.70438 13.2869 7.68163 13.2834 9.88395L13.2781 13.0442L12.7209 12.19L12.162 11.3358V9.03853C12.162 7.55332 12.169 6.71844 12.1796 6.67802C12.2077 6.57959 12.2692 6.50225 12.3536 6.45655C12.4256 6.41964 12.452 6.41613 12.728 6.41613C12.9881 6.41613 13.0338 6.41964 13.0918 6.45128Z"
fill="currentColor"
/>
<path
d="M14.7861 17.6141C14.7246 17.6527 14.7053 17.6791 14.7598 17.6492C14.7984 17.6264 14.8617 17.5789 14.8512 17.5771C14.8459 17.5771 14.816 17.5947 14.7861 17.6141ZM14.6648 17.6932C14.6332 17.7178 14.6332 17.7195 14.6719 17.7002C14.693 17.6896 14.7105 17.6773 14.7105 17.6738C14.7105 17.6598 14.7018 17.6633 14.6648 17.6932ZM14.577 17.7459C14.5453 17.7705 14.5453 17.7722 14.584 17.7529C14.6051 17.7424 14.6227 17.7301 14.6227 17.7265C14.6227 17.7125 14.6139 17.716 14.577 17.7459ZM14.4891 17.7986C14.4574 17.8232 14.4574 17.825 14.4961 17.8056C14.5172 17.7951 14.5348 17.7828 14.5348 17.7793C14.5348 17.7652 14.526 17.7687 14.4891 17.7986ZM14.3555 17.8689C14.2887 17.9041 14.2922 17.9181 14.359 17.8847C14.3889 17.8689 14.4117 17.8531 14.4117 17.8496C14.4117 17.8373 14.41 17.839 14.3555 17.8689Z"
fill="currentColor"
/>
</svg>
)
}
export default IconNextjs

View File

@@ -0,0 +1,24 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconPuzzleSolid = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M9.375 4.4475C9.375 4.15167 9.22 3.88417 9.04083 3.64833C8.85426 3.40948 8.752 3.11558 8.75 2.8125C8.75 1.94917 9.58917 1.25 10.625 1.25C11.6608 1.25 12.5 1.95 12.5 2.8125C12.5 3.12 12.3933 3.40667 12.2092 3.64833C12.03 3.88417 11.875 4.15167 11.875 4.4475C11.875 4.72417 12.1067 4.94583 12.3833 4.92917C13.975 4.83417 15.5417 4.64417 17.0767 4.36583C17.1603 4.35068 17.2461 4.35273 17.3289 4.37186C17.4117 4.39099 17.4897 4.42681 17.5582 4.47712C17.6267 4.52742 17.6842 4.59117 17.7272 4.66445C17.7702 4.73773 17.7978 4.81902 17.8083 4.90333C17.9969 6.4108 18.1015 7.92758 18.1217 9.44667C18.1224 9.51927 18.1088 9.59129 18.0814 9.65856C18.0541 9.72582 18.0137 9.78698 17.9625 9.83848C17.9113 9.88997 17.8504 9.93077 17.7833 9.9585C17.7162 9.98623 17.6443 10.0003 17.5717 10C17.2767 10 17.0092 9.845 16.7733 9.66583C16.5345 9.47926 16.2406 9.377 15.9375 9.375C15.075 9.375 14.375 10.2142 14.375 11.25C14.375 12.2858 15.075 13.125 15.9375 13.125C16.245 13.125 16.5317 13.0183 16.7733 12.8342C17.0092 12.655 17.2767 12.5 17.5725 12.5C17.8308 12.5 18.0367 12.7183 18.0175 12.9758C17.9197 14.3291 17.7542 15.6766 17.5217 17.0133C17.4996 17.1401 17.4389 17.2569 17.3479 17.3479C17.2569 17.4389 17.1401 17.4996 17.0133 17.5217C15.4967 17.7858 13.9525 17.9658 12.3842 18.0567C12.3185 18.0602 12.2528 18.0503 12.1911 18.0275C12.1294 18.0047 12.0729 17.9696 12.0253 17.9243C11.9776 17.879 11.9397 17.8244 11.9139 17.7639C11.888 17.7034 11.8748 17.6383 11.875 17.5725C11.875 17.2767 12.03 17.0092 12.2092 16.7733C12.3933 16.5317 12.5 16.245 12.5 15.9375C12.5 15.075 11.6608 14.375 10.625 14.375C9.58917 14.375 8.75 15.075 8.75 15.9375C8.75 16.245 8.85667 16.5317 9.04083 16.7733C9.22 17.0092 9.375 17.2767 9.375 17.5725C9.37525 17.644 9.36114 17.7148 9.33351 17.7808C9.30589 17.8467 9.2653 17.9064 9.21417 17.9564C9.16303 18.0064 9.10239 18.0456 9.03583 18.0717C8.96926 18.0978 8.89814 18.1102 8.82667 18.1083C7.51484 18.0713 6.20555 17.9712 4.90333 17.8083C4.81902 17.7978 4.73773 17.7702 4.66445 17.7272C4.59117 17.6842 4.52742 17.6267 4.47712 17.5582C4.42681 17.4897 4.39099 17.4117 4.37186 17.3289C4.35273 17.2461 4.35068 17.1603 4.36583 17.0767C4.61 15.7317 4.78583 14.3625 4.89 12.9733C4.89436 12.9127 4.88614 12.8517 4.86586 12.7944C4.84557 12.737 4.81365 12.6845 4.77211 12.64C4.73056 12.5956 4.68028 12.5602 4.62443 12.5361C4.56857 12.512 4.50833 12.4997 4.4475 12.5C4.15167 12.5 3.88417 12.655 3.64833 12.8342C3.40667 13.0183 3.12 13.125 2.8125 13.125C1.94917 13.125 1.25 12.2858 1.25 11.25C1.25 10.2142 1.95 9.375 2.8125 9.375C3.12 9.375 3.40667 9.48167 3.64833 9.66583C3.88417 9.845 4.15167 10 4.4475 10C4.51999 10.0003 4.59183 9.98625 4.65883 9.95856C4.72582 9.93087 4.78665 9.89013 4.83775 9.83872C4.88886 9.7873 4.92922 9.72624 4.95651 9.65907C4.98379 9.59191 4.99744 9.51999 4.99667 9.4475C4.97844 8.10511 4.89221 6.7645 4.73833 5.43083C4.72743 5.33729 4.7378 5.2425 4.76868 5.15353C4.79956 5.06457 4.85015 4.98373 4.91666 4.91706C4.98317 4.85039 5.06388 4.79961 5.15277 4.76852C5.24167 4.73742 5.33643 4.72682 5.43 4.7375C6.54917 4.86667 7.6825 4.94917 8.8275 4.9825C8.89886 4.9844 8.96988 4.97195 9.03634 4.94589C9.1028 4.91983 9.16336 4.8807 9.21441 4.83081C9.26547 4.78092 9.30599 4.72128 9.33358 4.65544C9.36116 4.5896 9.37525 4.51889 9.375 4.4475Z"
fill="currentColor"
/>
</svg>
)
}
export default IconPuzzleSolid

View File

@@ -0,0 +1,24 @@
import { IconProps } from "@medusajs/icons/dist/types"
import clsx from "clsx"
import React from "react"
const IconTwitter = (props: IconProps) => {
return (
<svg
width={props.width || 20}
height={props.height || 20}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={clsx("text-ui-fg-subtle", props.className)}
>
<path
d="M18.5921 4.33226C18.3093 4.45768 18.0186 4.56353 17.7215 4.64947C18.0732 4.25173 18.3413 3.78373 18.505 3.2716C18.5417 3.15681 18.5037 3.03116 18.4093 2.95608C18.3151 2.88094 18.1841 2.87194 18.0804 2.93344C17.4495 3.3076 16.7689 3.5765 16.0552 3.73374C15.3363 3.03127 14.3599 2.6315 13.3505 2.6315C11.2199 2.6315 9.4864 4.3649 9.4864 6.49551C9.4864 6.66332 9.49703 6.83019 9.51805 6.99475C6.87409 6.76261 4.41605 5.46307 2.72812 3.39255C2.66797 3.31875 2.5753 3.27898 2.48042 3.28658C2.38549 3.29402 2.30019 3.34755 2.25223 3.42983C1.90988 4.01726 1.72889 4.68913 1.72889 5.37272C1.72889 6.30378 2.06131 7.18717 2.64852 7.87744C2.46997 7.81561 2.2967 7.73832 2.13134 7.64652C2.04256 7.59711 1.93421 7.59786 1.84601 7.64844C1.75775 7.69901 1.70242 7.79203 1.70009 7.8937C1.69969 7.91083 1.69969 7.92796 1.69969 7.94532C1.69969 9.33509 2.44767 10.5863 3.59125 11.2683C3.493 11.2585 3.39482 11.2442 3.29727 11.2256C3.1967 11.2064 3.09329 11.2416 3.02547 11.3183C2.95754 11.395 2.93506 11.5018 2.96636 11.5994C3.38965 12.9209 4.47946 13.893 5.79694 14.1893C4.70423 14.8737 3.45462 15.2322 2.14283 15.2322C1.86912 15.2322 1.59384 15.2161 1.32442 15.1843C1.19058 15.1684 1.06261 15.2474 1.01703 15.3747C0.971445 15.502 1.01975 15.644 1.13362 15.7169C2.81882 16.7975 4.7674 17.3686 6.76859 17.3686C10.7027 17.3686 13.1637 15.5134 14.5355 13.9571C16.2461 12.0166 17.2271 9.44797 17.2271 6.91003C17.2271 6.80401 17.2255 6.69694 17.2223 6.59021C17.8971 6.08174 18.4782 5.46638 18.951 4.7591C19.0228 4.65168 19.0151 4.50971 18.9318 4.41083C18.8488 4.31188 18.7103 4.27989 18.5921 4.33226Z"
fill="currentColor"
/>
</svg>
)
}
export default IconTwitter

View File

@@ -0,0 +1,210 @@
import {
AcademicCapSolid,
Adjustments,
ArrowDownLeftMini,
ArrowDownTray,
ArrowUpRightOnBox,
ArrowUturnLeft,
BarsThree,
BellAlert,
BellAlertSolid,
Bolt,
BoltSolid,
BookOpen,
Bug,
BugAntSolid,
BuildingStorefront,
BuildingTax,
BuildingsSolid,
Calendar,
CashSolid,
Channels,
ChannelsSolid,
CheckCircleSolid,
CheckMini,
ChevronDoubleLeftMiniSolid,
ChevronUpDown,
CircleDottedLine,
CircleMiniSolid,
CircleStack,
CircleStackSolid,
ClockSolidMini,
CloudArrowUp,
CogSixTooth,
CogSixToothSolid,
CommandLine,
CommandLineSolid,
ComponentSolid,
ComputerDesktop,
ComputerDesktopSolid,
CreditCardSolid,
CubeSolid,
CurrencyDollar,
CurrencyDollarSolid,
Discord,
DocumentText,
DocumentTextSolid,
EllipseMiniSolid,
ExclamationCircle,
ExclamationCircleSolid,
FlyingBox,
Folder,
FolderOpen,
Gatsby,
GiftSolid,
Github,
GlobeEurope,
GlobeEuropeSolid,
InformationCircleSolid,
JavascriptEx,
Key,
KeySolid,
LightBulb,
LightBulbSolid,
Linkedin,
MagnifyingGlass,
Map,
Moon,
Newspaper,
PencilSquareSolid,
Puzzle,
ReactJsEx,
ReceiptPercent,
RocketLaunch,
Server,
ServerSolid,
ServerStack,
ServerStackSolid,
ShoppingCart,
ShoppingCartSolid,
Sidebar,
Sparkles,
SparklesSolid,
SquareTwoStackSolid,
SquaresPlus,
SquaresPlusSolid,
Star,
StarSolid,
Stripe,
Sun,
SwatchSolid,
TagSolid,
Tools,
ToolsSolid,
Twitter,
User,
UsersSolid,
XCircleSolid,
XMark,
XMarkMini,
PhotoSolid,
} from "@medusajs/icons"
import IconPuzzleSolid from "./PuzzleSolid"
import IconNextjs from "./Nextjs"
export default {
"academic-cap-solid": AcademicCapSolid,
adjustments: Adjustments,
alert: ExclamationCircleSolid,
"arrow-down-left-mini": ArrowDownLeftMini,
"arrow-down-tray": ArrowDownTray,
"back-arrow": ArrowUturnLeft,
"bars-three": BarsThree,
bell: BellAlert,
"bell-alert-solid": BellAlertSolid,
bolt: Bolt,
"bolt-solid": BoltSolid,
"book-open": BookOpen,
bug: Bug,
"bug-ant-solid": BugAntSolid,
"building-solid": BuildingsSolid,
"building-storefront": BuildingStorefront,
"building-tax": BuildingTax,
calendar: Calendar,
"cash-solid": CashSolid,
"channels-solid": ChannelsSolid,
channels: Channels,
"check-circle-solid": CheckCircleSolid,
"check-mini": CheckMini,
"chevron-double-left-mini-solid": ChevronDoubleLeftMiniSolid,
"chevron-up-down": ChevronUpDown,
"circle-dotted-line": CircleDottedLine,
"circle-mini-solid": CircleMiniSolid,
"circle-stack": CircleStack,
"circle-stack-solid": CircleStackSolid,
"clock-solid-mini": ClockSolidMini,
close: XMark,
"cloud-arrow-up": CloudArrowUp,
"cog-six-tooth": CogSixTooth,
"cog-six-tooth-solid": CogSixToothSolid,
"command-line": CommandLine,
"command-line-solid": CommandLineSolid,
"component-solid": ComponentSolid,
"computer-desktop": ComputerDesktop,
"computer-desktop-solid": ComputerDesktopSolid,
copy: SquareTwoStackSolid,
"credit-card-solid": CreditCardSolid,
"cube-solid": CubeSolid,
"currency-dollar": CurrencyDollar,
"currency-dollar-solid": CurrencyDollarSolid,
"dark-mode": Moon,
discord: Discord,
"document-text": DocumentText,
"document-text-solid": DocumentTextSolid,
"ellipse-mini-solid": EllipseMiniSolid,
"exclamation-circle-solid": ExclamationCircleSolid,
"external-link": ArrowUpRightOnBox,
"flying-box": FlyingBox,
folder: Folder,
"folder-open": FolderOpen,
gatsby: Gatsby,
"gift-solid": GiftSolid,
github: Github,
"globe-europe": GlobeEurope,
"globe-europe-solid": GlobeEuropeSolid,
"information-circle-solid": InformationCircleSolid,
javascript: JavascriptEx,
key: Key,
"key-solid": KeySolid,
"light-bulb": LightBulb,
"light-bulb-solid": LightBulbSolid,
"light-mode": Sun,
linkedin: Linkedin,
"magnifying-glass": MagnifyingGlass,
map: Map,
newspaper: Newspaper,
nextjs: IconNextjs,
"pencil-square-solid": PencilSquareSolid,
"photo-solid": PhotoSolid,
puzzle: Puzzle,
// TODO change once available in Icons package
"puzzle-solid": IconPuzzleSolid,
react: ReactJsEx,
"receipt-percent": ReceiptPercent,
report: ExclamationCircle,
"rocket-launch": RocketLaunch,
server: Server,
"server-solid": ServerSolid,
"server-stack": ServerStack,
"server-stack-solid": ServerStackSolid,
"shopping-cart": ShoppingCart,
"shopping-cart-solid": ShoppingCartSolid,
sidebar: Sidebar,
sparkles: Sparkles,
"sparkles-solid": SparklesSolid,
"squares-plus": SquaresPlus,
"squares-plus-solid": SquaresPlusSolid,
star: Star,
"star-solid": StarSolid,
stripe: Stripe,
"swatch-solid": SwatchSolid,
"tag-solid": TagSolid,
tools: Tools,
"tools-solid": ToolsSolid,
twitter: Twitter,
user: User,
"users-solid": UsersSolid,
"x-circle-solid": XCircleSolid,
"x-mark": XMark,
"x-mark-mini": XMarkMini,
}

View File

@@ -0,0 +1,74 @@
import React, { useEffect } from "react"
import clsx from "clsx"
import ErrorBoundary from "@docusaurus/ErrorBoundary"
import {
PageMetadata,
SkipToContentFallbackId,
ThemeClassNames,
} from "@docusaurus/theme-common"
import { useKeyboardNavigation } from "@docusaurus/theme-common/internal"
import SkipToContent from "@theme/SkipToContent"
import Navbar from "@theme/Navbar"
import LayoutProvider from "@theme/Layout/Provider"
import ErrorPageContent from "@theme/ErrorPageContent"
import type { Props } from "@theme/Layout"
import useIsBrowser from "@docusaurus/useIsBrowser"
import { useLocation } from "@docusaurus/router"
import { useAnalytics } from "docs-ui"
export default function Layout(props: Props): JSX.Element {
const {
children,
wrapperClassName,
// Not really layout-related, but kept for convenience/retro-compatibility
title,
description,
} = props
useKeyboardNavigation()
const isBrowser = useIsBrowser()
const location = useLocation()
const { track } = useAnalytics()
useEffect(() => {
if (isBrowser) {
const handlePlay = () => {
track("video_played")
}
const videos = document.querySelectorAll("video")
videos.forEach((video) =>
video.addEventListener("play", handlePlay, {
once: true,
capture: true,
})
)
return () => {
videos.forEach((video) => video.removeEventListener("play", handlePlay))
}
}
}, [isBrowser, location.pathname])
return (
<LayoutProvider>
<PageMetadata title={title} description={description} />
<SkipToContent />
<Navbar />
<div
id={SkipToContentFallbackId}
className={clsx(
ThemeClassNames.wrapper.main,
"flex-auto flex-grow flex-shrink-0",
wrapperClassName
)}
>
<ErrorBoundary fallback={(params) => <ErrorPageContent {...params} />}>
{children}
</ErrorBoundary>
</div>
</LayoutProvider>
)
}

View File

@@ -0,0 +1,48 @@
import React, { useMemo } from "react"
import type { Props } from "@theme/MDXComponents/A"
import { getGlossaryByPath } from "../../utils/glossary"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import { MedusaDocusaurusContext } from "@medusajs/docs"
import Link from "@docusaurus/Link"
import clsx from "clsx"
import { Tooltip } from "docs-ui"
const MDXA = (props: Props) => {
const { href, children } = props
const {
siteConfig: { url },
} = useDocusaurusContext() as MedusaDocusaurusContext
// check if a glossary exists for href
const glossary = useMemo(() => {
return (typeof children === "string" && href.startsWith("/")) ||
href.includes(url)
? getGlossaryByPath(children as string)
: null
}, [href, children])
if (!glossary) {
return <Link {...props} />
}
return (
<Tooltip
tooltipChildren={
<span className="flex flex-col gap-0.25 max-w-[200px]">
<span className="text-compact-small-plus text-medusa-fg-base dark:text-medusa-fg-base-dark">
{glossary.title}
</span>
<span className="text-compact-small">{glossary.content}</span>
</span>
}
clickable={true}
>
<Link
{...props}
className={clsx(props.className, "border-0 border-b border-dashed")}
/>
</Tooltip>
)
}
export default MDXA

View File

@@ -0,0 +1,24 @@
import React, { type ComponentProps, type ReactElement } from "react"
import type { Props } from "@theme/MDXComponents/Details"
import Details, { DetailsProps } from "../Details"
import { type DetailsSummaryProps } from "docs-ui"
type MDXDetailsProps = Props & DetailsProps
export default function MDXDetails(props: MDXDetailsProps): JSX.Element {
const items = React.Children.toArray(props.children)
// Split summary item from the rest to pass it as a separate prop to the
// Details theme component
const summary = items.find(
(item): item is ReactElement<ComponentProps<"summary">> =>
React.isValidElement(item) &&
(item.props as { mdxType: string } | null)?.mdxType === "summary"
) as React.ReactElement<DetailsSummaryProps>
const children = <>{items.filter((item) => item !== summary)}</>
return (
<Details summary={summary} {...props}>
{children}
</Details>
)
}

View File

@@ -0,0 +1,17 @@
// Import the original mapper
import MDXComponents from "@theme-original/MDXComponents"
import CloudinaryImage from "@site/src/components/CloudinaryImage"
import MDXDetails from "./Details"
import MDXA from "./A"
import { Kbd, DetailsSummary, InlineCode } from "docs-ui"
export default {
// Re-use the default mapping
...MDXComponents,
inlineCode: InlineCode,
img: CloudinaryImage,
details: MDXDetails,
summary: DetailsSummary,
a: MDXA,
kbd: Kbd,
}

View File

@@ -0,0 +1,79 @@
import React from "react"
import Link from "@docusaurus/Link"
import useBaseUrl from "@docusaurus/useBaseUrl"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import { useThemeConfig, type NavbarLogo } from "@docusaurus/theme-common"
import ThemedImage from "@theme/ThemedImage"
import type { Props } from "@theme/Logo"
import { ThemeConfig } from "@medusajs/docs"
function LogoThemedImage({
logo,
alt,
imageClassName,
}: {
logo: NavbarLogo
alt: string
imageClassName?: string
}) {
const sources = {
light: useBaseUrl(logo.src),
dark: useBaseUrl(logo.srcDark || logo.src),
}
const themedImage = (
<ThemedImage
className={logo.className}
sources={sources}
height={logo.height}
width={logo.width}
alt={alt}
style={logo.style}
/>
)
// Is this extra div really necessary?
// introduced in https://github.com/facebook/docusaurus/pull/5666
return imageClassName ? (
<div className={imageClassName}>{themedImage}</div>
) : (
themedImage
)
}
export default function MobileLogo(props: Props): JSX.Element {
const {
siteConfig: { title },
} = useDocusaurusContext()
const {
navbar: { title: navbarTitle },
mobileLogo: logo,
} = useThemeConfig() as ThemeConfig
const { imageClassName, titleClassName, ...propsRest } = props
const logoLink = useBaseUrl(logo?.href || "/")
// If visible title is shown, fallback alt text should be
// an empty string to mark the logo as decorative.
const fallbackAlt = navbarTitle ? "" : title
// Use logo alt text if provided (including empty string),
// and provide a sensible fallback otherwise.
const alt = logo?.alt ?? fallbackAlt
return (
<Link
to={logoLink}
{...propsRest}
{...(logo?.target && { target: logo.target })}
>
{logo && (
<LogoThemedImage
logo={logo}
alt={alt}
imageClassName={imageClassName}
/>
)}
{navbarTitle != null && <b className={titleClassName}>{navbarTitle}</b>}
</Link>
)
}

View File

@@ -0,0 +1,27 @@
import React from "react"
import { useColorMode, useThemeConfig } from "@docusaurus/theme-common"
import ColorModeToggle from "@theme/ColorModeToggle"
import type { Props } from "@theme/Navbar/ColorModeToggle"
import clsx from "clsx"
export default function NavbarColorModeToggle({
className,
}: Props): JSX.Element | null {
const disabled = useThemeConfig().colorMode.disableSwitch
const { colorMode, setColorMode } = useColorMode()
if (disabled) {
return null
}
return (
<ColorModeToggle
className={clsx("text-ui-fg-muted", className)}
buttonClassName={clsx(
"hover:!bg-medusa-button-neutral-hover dark:hover:!bg-medusa-button-neutral-hover-dark"
)}
value={colorMode}
onChange={setColorMode}
/>
)
}

View File

@@ -0,0 +1,167 @@
import React, { type ReactNode } from "react"
import { useThemeConfig } from "@docusaurus/theme-common"
import {
splitNavbarItems,
useNavbarMobileSidebar,
} from "@docusaurus/theme-common/internal"
import NavbarItem, { type Props as NavbarItemConfig } from "@theme/NavbarItem"
import NavbarColorModeToggle from "@theme/Navbar/ColorModeToggle"
import NavbarMobileSidebarToggle from "@theme/Navbar/MobileSidebar/Toggle"
import NavbarLogo from "@theme/Navbar/Logo"
import NavbarActions from "../../../components/Navbar/Actions"
import { ThemeConfig } from "@medusajs/docs"
import useIsBrowser from "@docusaurus/useIsBrowser"
import clsx from "clsx"
import { useSidebar } from "../../../providers/Sidebar"
import { Tooltip } from "docs-ui"
import { ChevronDoubleLeftMiniSolid, Sidebar } from "@medusajs/icons"
function useNavbarItems() {
// TODO temporary casting until ThemeConfig type is improved
return useThemeConfig().navbar.items as NavbarItemConfig[]
}
function NavbarItems({ items }: { items: NavbarItemConfig[] }): JSX.Element {
return (
<>
{items.map((item, i) => (
<NavbarItem {...item} key={i} />
))}
</>
)
}
function NavbarContentLayout({
left,
right,
}: {
left: ReactNode
right: ReactNode
}) {
return (
<div
className={clsx(
"flex flex-wrap justify-between items-center w-full",
"lg:max-w-xl mx-auto py-0.5 px-1"
)}
>
<div className={clsx("items-center flex flex-1 min-w-0 gap-1.5")}>
{left}
</div>
<div
className={clsx("items-center flex lg:flex-1 min-w-0", "justify-end")}
>
{right}
</div>
</div>
)
}
export default function NavbarContent(): JSX.Element {
const mobileSidebar = useNavbarMobileSidebar()
const items = useNavbarItems()
const [leftItems, rightItems] = splitNavbarItems(items)
const {
navbarActions,
docs: {
sidebar: { hideable },
},
} = useThemeConfig() as ThemeConfig
const sidebarContext = useSidebar()
const isBrowser = useIsBrowser()
const isApple = isBrowser
? navigator.userAgent.toLowerCase().indexOf("mac") !== 0
: true
return (
<NavbarContentLayout
left={
// TODO stop hardcoding items?
<>
{!mobileSidebar.disabled && <NavbarMobileSidebarToggle />}
<NavbarLogo />
<NavbarItems items={leftItems} />
</>
}
right={
// TODO stop hardcoding items?
// Ask the user to add the respective navbar items => more flexible
<div className="flex gap-0.5">
<NavbarItems items={rightItems} />
{hideable && sidebarContext?.hasSidebar && (
<NavbarActions
items={[
{
type: "button",
html: !sidebarContext?.hiddenSidebarContainer
? `<span class="text-compact-x-small-plus">Close sidebar <kbd class="${clsx(
"bg-medusa-tag-neutral-bg dark:bg-medusa-tag-neutral-bg-dark",
"border border-solid rounded border-medusa-tag-neutral-border dark:border-medusa-tag-neutral-border-dark",
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark font-base text-compact-x-small-plus",
"inline-flex w-[22px] h-[22px] !p-0 justify-center items-center shadow-none ml-0.5"
)}">${isApple ? "⌘" : "Ctrl"}</kbd>
<kbd class="${clsx(
"bg-medusa-tag-neutral-bg dark:bg-medusa-tag-neutral-bg-dark",
"border border-solid rounded border-medusa-tag-neutral-border dark:border-medusa-tag-neutral-border-dark",
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark font-base text-compact-x-small-plus",
"inline-flex w-[22px] h-[22px] !p-0 justify-center items-center shadow-none"
)}">I</kbd></span>`
: `<span class="text-compact-x-small-plus">Lock sidebar open <kbd class="${clsx(
"bg-medusa-tag-neutral-bg dark:bg-medusa-tag-neutral-bg-dark",
"border border-solid rounded border-medusa-tag-neutral-border dark:border-medusa-tag-neutral-border-dark",
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark font-base text-compact-x-small-plus",
"inline-flex w-[22px] h-[22px] !p-0 justify-center items-center shadow-none ml-0.5"
)}">${isApple ? "⌘" : "Ctrl"}</kbd>
<kbd class="${clsx(
"bg-medusa-tag-neutral-bg dark:bg-medusa-tag-neutral-bg-dark",
"border border-solid rounded border-medusa-tag-neutral-border dark:border-medusa-tag-neutral-border-dark",
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark font-base text-compact-x-small-plus",
"inline-flex w-[22px] h-[22px] !p-0 justify-center items-center shadow-none"
)}">I</kbd></span>`,
events: {
onClick: sidebarContext?.onCollapse,
onMouseEnter: () => {
if (!sidebarContext?.hiddenSidebarContainer) {
sidebarContext?.setFloatingSidebar(false)
} else {
sidebarContext?.setFloatingSidebar(true)
}
},
onMouseLeave: () => {
setTimeout(() => {
if (
!document.querySelector(
".theme-doc-sidebar-container:hover"
)
) {
sidebarContext?.setFloatingSidebar(false)
}
}, 100)
},
},
Icon: !sidebarContext?.hiddenSidebarContainer ? (
<Sidebar className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark" />
) : (
<ChevronDoubleLeftMiniSolid className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark" />
),
buttonType: "icon",
},
]}
className="sidebar-toggler"
/>
)}
<Tooltip text="Switch theme">
<NavbarColorModeToggle
className={clsx(
"navbar-action-icon-item !w-2 !h-2 [&>button]:!rounded"
)}
/>
</Tooltip>
<NavbarActions items={navbarActions} />
</div>
}
/>
)
}

View File

@@ -0,0 +1,44 @@
import React from "react"
import clsx from "clsx"
import { useThemeConfig } from "@docusaurus/theme-common"
import {
useHideableNavbar,
useNavbarMobileSidebar,
} from "@docusaurus/theme-common/internal"
import { translate } from "@docusaurus/Translate"
import NavbarMobileSidebar from "@theme/Navbar/MobileSidebar"
import type { Props } from "@theme/Navbar/Layout"
export default function NavbarLayout({ children }: Props): JSX.Element {
const {
navbar: { hideOnScroll, style },
} = useThemeConfig()
const mobileSidebar = useNavbarMobileSidebar()
const { navbarRef, isNavbarVisible } = useHideableNavbar(hideOnScroll)
return (
<nav
ref={navbarRef}
aria-label={translate({
id: "theme.NavBar.navAriaLabel",
message: "Main",
description: "The ARIA label for the main navigation",
})}
className={clsx(
"navbar",
"navbar--fixed-top",
hideOnScroll && [
"transition-transform",
!isNavbarVisible && "translate-x-0 translate-y-[calc(-100%-2px)]",
],
{
"navbar--dark": style === "dark",
"navbar--primary": style === "primary",
"navbar-sidebar--show": mobileSidebar.shown,
}
)}
>
{children}
<NavbarMobileSidebar />
</nav>
)
}

View File

@@ -0,0 +1,20 @@
import React from "react"
import Logo from "@theme/Logo"
import MobileLogo from "../../MobileLogo"
export default function NavbarLogo(): JSX.Element {
return (
<>
<Logo
className="navbar__brand hidden lg:block"
imageClassName="navbar__logo"
titleClassName="navbar__title text--truncate"
/>
<MobileLogo
className="navbar__brand lg:hidden mx-auto"
imageClassName="navbar__logo"
titleClassName="navbar__title text--truncate"
/>
</>
)
}

View File

@@ -0,0 +1,42 @@
import React from "react"
import { useNavbarMobileSidebar } from "@docusaurus/theme-common/internal"
import NavbarColorModeToggle from "@theme/Navbar/ColorModeToggle"
import IconClose from "@theme/Icon/Close"
import NavbarLogo from "@theme/Navbar/Logo"
import clsx from "clsx"
function CloseButton() {
const mobileSidebar = useNavbarMobileSidebar()
return (
<button
type="button"
className={clsx(
"bg-transparent border-0 text-inherit cursor-pointer p-0",
"flex ml-auto"
)}
onClick={() => mobileSidebar.toggle()}
>
<IconClose color="var(--ifm-color-emphasis-600)" />
</button>
)
}
export default function NavbarMobileSidebarHeader(): JSX.Element {
return (
<div
className={clsx(
"items-center shadow-navbar dark:shadow-navbar-dark",
"flex flex-1 h-navbar py-0.75 px-1.5"
)}
>
<NavbarLogo />
<NavbarColorModeToggle
className={clsx(
"[&>button]:hover:bg-medusa-button-neutral-hover dark:[&>button]:hover:bg-medusa-button-neutral-hover-dark",
"[&>button]:!rounded"
)}
/>
<CloseButton />
</div>
)
}

View File

@@ -0,0 +1,23 @@
import React from "react"
import clsx from "clsx"
import { useNavbarSecondaryMenu } from "@docusaurus/theme-common/internal"
import type { Props } from "@theme/Navbar/MobileSidebar/Layout"
export default function NavbarMobileSidebarLayout({
primaryMenu,
secondaryMenu,
}: Props): JSX.Element {
const { shown: secondaryMenuShown } = useNavbarSecondaryMenu()
return (
<div className="navbar-sidebar top-[57px] shadow-none">
<div
className={clsx("navbar-sidebar__items", {
"navbar-sidebar__items--show-secondary": secondaryMenuShown,
})}
>
<div className="navbar-sidebar__item menu">{primaryMenu}</div>
<div className="navbar-sidebar__item menu">{secondaryMenu}</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,29 @@
import React from "react"
import { useNavbarMobileSidebar } from "@docusaurus/theme-common/internal"
import { translate } from "@docusaurus/Translate"
import { Sidebar, XMark } from "@medusajs/icons"
export default function MobileSidebarToggle(): JSX.Element {
const { toggle, shown } = useNavbarMobileSidebar()
return (
<button
onClick={toggle}
aria-label={translate({
id: "theme.docs.sidebar.toggleSidebarButtonAriaLabel",
message: "Toggle navigation bar",
description:
"The ARIA label for hamburger menu button of mobile navigation",
})}
aria-expanded={shown}
className="navbar__toggle !block lg:!hidden clean-btn"
type="button"
>
{!shown && (
<Sidebar className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark" />
)}
{shown && (
<XMark className="text-medusa-fg-muted dark:text-medusa-fg-muted-dark" />
)}
</button>
)
}

View File

@@ -0,0 +1,10 @@
import React from "react"
import clsx from "clsx"
import type { Props } from "@theme/Navbar/Search"
export default function NavbarSearch({
children,
className,
}: Props): JSX.Element {
return <div className={clsx("flex", className)}>{children}</div>
}

View File

@@ -0,0 +1,55 @@
import React from "react"
import Link from "@docusaurus/Link"
import useBaseUrl from "@docusaurus/useBaseUrl"
import { isRegexpStringMatch } from "@docusaurus/theme-common"
import type { Props } from "@theme/NavbarItem/NavbarNavLink"
export default function NavbarNavLink({
activeBasePath,
activeBaseRegex,
to,
href,
label,
html,
prependBaseUrlToHref,
...props
}: Props): JSX.Element {
// TODO all this seems hacky
// {to: 'version'} should probably be forbidden, in favor of {to: '/version'}
const toUrl = useBaseUrl(to)
const activeBaseUrl = useBaseUrl(activeBasePath)
const normalizedHref = useBaseUrl(href, { forcePrependBaseUrl: true })
// Link content is set through html XOR label
const linkContentProps = html
? { dangerouslySetInnerHTML: { __html: html } }
: {
children: <>{label}</>,
}
if (href) {
return (
<Link
href={prependBaseUrlToHref ? normalizedHref : href}
{...props}
{...linkContentProps}
/>
)
}
return (
<Link
to={toUrl}
isNavLink
{...((activeBasePath || activeBaseRegex) && {
isActive: (_match, location) => {
return activeBaseRegex
? isRegexpStringMatch(activeBaseRegex, location.pathname)
: location.pathname.startsWith(activeBaseUrl)
},
})}
{...props}
{...linkContentProps}
/>
)
}

View File

@@ -0,0 +1,17 @@
import React from "react"
import NavbarSearch from "@theme/Navbar/Search"
import type { Props } from "@theme/NavbarItem/SearchNavbarItem"
import { SearchModalOpener } from "docs-ui"
import { useWindowSize } from "@docusaurus/theme-common"
export default function SearchNavbarItem({
mobile,
}: Props): JSX.Element | null {
const windowSize = useWindowSize()
return (
<NavbarSearch>
<SearchModalOpener isMobile={mobile || windowSize !== "desktop"} />
</NavbarSearch>
)
}

View File

@@ -0,0 +1,86 @@
import React from "react"
import Translate, { translate } from "@docusaurus/Translate"
import { PageMetadata } from "@docusaurus/theme-common"
import Layout from "@theme/Layout"
import useBaseUrl from "@docusaurus/useBaseUrl"
import DocsProviders from "../providers/DocsProviders"
export default function NotFound(): JSX.Element {
return (
<DocsProviders>
<PageMetadata
title={translate({
id: "theme.NotFound.title",
message: "Page Not Found",
})}
/>
<Layout>
<main className="container markdown theme-doc-markdown my-4">
<div className="row">
<div className="col col--6 col--offset-3">
<h1>
<Translate
id="theme.NotFound.title"
description="The title of the 404 page"
>
Page Not Found
</Translate>
</h1>
<p>
<Translate
id="theme.NotFound.p1"
description="The first paragraph of the 404 page"
>
Looks like the page you&apos;re looking for has either changed
into a different location or isn&apos;t in our documentation
anymore.
</Translate>
</p>
<p>
If you think this is a mistake, please{" "}
<a
href="https://github.com/medusajs/medusa/issues/new?assignees=&labels=type%3A+docs&template=docs.yml"
rel="noopener noreferrer"
target="_blank"
>
report this issue on GitHub
</a>
</p>
<h2>Some popular links</h2>
<ul>
<li>
<a href={useBaseUrl("/usage/create-medusa-app")}>
Install Medusa with create-medusa-app
</a>
</li>
<li>
<a href="https://docs.medusajs.com/api/store">
Storefront REST API Reference
</a>
</li>
<li>
<a href="https://docs.medusajs.com/api/admin">
Admin REST API Reference
</a>
</li>
<li>
<a href={useBaseUrl("/starters/nextjs-medusa-starter")}>
Install Next.js Storefront
</a>
</li>
<li>
<a href={useBaseUrl("/admin/quickstart")}>
Install Medusa Admin
</a>
</li>
<li>
<a href={useBaseUrl("/user-guide")}>User Guide</a>
</li>
</ul>
</div>
</div>
</main>
</Layout>
</DocsProviders>
)
}

View File

@@ -0,0 +1,51 @@
import React, { useEffect } from "react"
import SearchBar from "@theme-original/SearchBar"
import type SearchBarType from "@theme/SearchBar"
import type { WrapperProps } from "@docusaurus/types"
import useIsBrowser from "@docusaurus/useIsBrowser"
import { useLocation } from "@docusaurus/router"
import { useAnalytics } from "docs-ui"
type Props = WrapperProps<typeof SearchBarType>
export default function SearchBarWrapper(props: Props): JSX.Element {
const isBrowser = useIsBrowser()
const location = useLocation()
const { track } = useAnalytics()
useEffect(() => {
if (isBrowser) {
const trackSearch = (e) => {
if (
!e.target.classList?.contains("DocSearch-Input") &&
!(
e.target.tagName.toLowerCase() === "input" &&
e.target.getAttribute("type") === "search"
)
) {
return
}
const query = e.target.value
if (query.length >= 3) {
// send event to segment
track("search", {
query,
})
}
}
document.body.addEventListener("keyup", trackSearch)
return () => {
document.body.removeEventListener("keyup", trackSearch)
}
}
}, [isBrowser, location.pathname])
return (
<>
<SearchBar {...props} />
</>
)
}

View File

@@ -0,0 +1,12 @@
import { translate } from "@docusaurus/Translate"
import translations from "@theme-original/SearchTranslations"
const changedTranslations = {
...translations,
placeholder: translate({
id: "theme.SearchModal.placeholder",
message: "Find something",
description: "The placeholder of the input of the DocSearch pop-up modal",
}),
}
export default changedTranslations

View File

@@ -0,0 +1,22 @@
import React from "react"
import TOCItems from "@theme-original/TOCItems"
import type TOCItemsType from "@theme/TOCItems"
import type { WrapperProps } from "@docusaurus/types"
import StructuredDataHowTo from "@site/src/components/StructuredData/HowTo"
import { useDoc } from "@docusaurus/theme-common/internal"
import { DocContextValue } from "@medusajs/docs"
type Props = WrapperProps<typeof TOCItemsType>
export default function TOCItemsWrapper(props: Props): JSX.Element {
const { frontMatter, contentTitle } = useDoc() as DocContextValue
return (
<>
<TOCItems {...props} />
{frontMatter?.addHowToData && (
<StructuredDataHowTo toc={props.toc} title={contentTitle} />
)}
</>
)
}

View File

@@ -0,0 +1,261 @@
import React, { ReactElement, cloneElement, useEffect, useRef } from "react"
import clsx from "clsx"
import {
useScrollPositionBlocker,
useTabs,
type TabItemProps,
} from "@docusaurus/theme-common/internal"
import useIsBrowser from "@docusaurus/useIsBrowser"
import type { Props as OldProps } from "@theme/Tabs"
// import styles from "./styles.module.css"
type TabsCustomProps = {
isCodeTabs?: boolean
codeTitle?: string
}
type TabListProps = OldProps & ReturnType<typeof useTabs> & TabsCustomProps
function TabList({
className,
selectedValue,
selectValue,
tabValues,
isCodeTabs = false,
codeTitle,
}: TabListProps) {
const tabRefs: (HTMLLIElement | null)[] = []
const { blockElementScrollPositionUntilNextRender } =
useScrollPositionBlocker()
const codeTabSelectorRef = useRef(null)
const codeTabsWrapperRef = useRef(null)
const handleTabChange = (
event:
| React.FocusEvent<HTMLLIElement>
| React.MouseEvent<HTMLLIElement>
| React.KeyboardEvent<HTMLLIElement>
) => {
const newTab = event.currentTarget
const newTabIndex = tabRefs.indexOf(newTab)
const newTabValue = tabValues[newTabIndex]!.value
if (newTabValue !== selectedValue) {
blockElementScrollPositionUntilNextRender(newTab)
selectValue(newTabValue)
}
}
const handleKeydown = (event: React.KeyboardEvent<HTMLLIElement>) => {
let focusElement: HTMLLIElement | null = null
switch (event.key) {
case "Enter": {
handleTabChange(event)
break
}
case "ArrowRight": {
const nextTab = tabRefs.indexOf(event.currentTarget) + 1
focusElement = tabRefs[nextTab] ?? tabRefs[0]!
break
}
case "ArrowLeft": {
const prevTab = tabRefs.indexOf(event.currentTarget) - 1
focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1]!
break
}
default:
break
}
focusElement?.focus()
}
const changeTabSelectorCoordinates = (selectedTab) => {
if (!codeTabSelectorRef?.current || !codeTabsWrapperRef?.current) {
return
}
const selectedTabsCoordinates = selectedTab.getBoundingClientRect()
const tabsWrapperCoordinates =
codeTabsWrapperRef.current.getBoundingClientRect()
codeTabSelectorRef.current.style.left = `${
selectedTabsCoordinates.left - tabsWrapperCoordinates.left
}px`
codeTabSelectorRef.current.style.width = `${selectedTabsCoordinates.width}px`
codeTabSelectorRef.current.style.height = `${selectedTabsCoordinates.height}px`
}
useEffect(() => {
if (codeTabSelectorRef?.current && tabRefs.length) {
const selectedTab = tabRefs.find(
(tab) => tab.getAttribute("aria-selected") === "true"
)
if (selectedTab) {
changeTabSelectorCoordinates(selectedTab)
}
}
}, [codeTabSelectorRef, tabRefs])
return (
<div
className={clsx(isCodeTabs && "code-header", !isCodeTabs && "[&+*]:pt-2")}
>
<div
className={clsx(isCodeTabs && "relative overflow-auto")}
ref={codeTabsWrapperRef}
>
{isCodeTabs && (
<span
className={clsx(
"xs:absolute xs:border xs:border-solid xs:border-medusa-code-border dark:xs:border-medusa-code-border-dark xs:bg-medusa-code-bg-base dark:xs:bg-medusa-code-bg-base-dark xs:transition-all xs:duration-200 xs:ease-ease xs:top-0 xs:z-[1] xs:rounded-full"
)}
ref={codeTabSelectorRef}
></span>
)}
<ul
role="tablist"
aria-orientation="horizontal"
className={clsx(
"tabs",
isCodeTabs && "no-scrollbar",
"list-none",
className
)}
>
{tabValues.map(({ value, label, attributes }) => (
<li
// TODO extract TabListItem
role="tab"
tabIndex={selectedValue === value ? 0 : -1}
aria-selected={selectedValue === value}
key={value}
ref={(tabControl) => tabRefs.push(tabControl)}
onKeyDown={handleKeydown}
onClick={handleTabChange}
{...attributes}
className={clsx(
isCodeTabs &&
"text-compact-small-plus py-0.25 border border-solid border-transparent whitespace-nowrap rounded-full [&:not(:first-child)]:ml-0.25",
"!mt-0 cursor-pointer",
attributes?.className,
isCodeTabs && "z-[2] flex justify-center items-center",
isCodeTabs &&
selectedValue !== value &&
"text-medusa-code-text-subtle dark:text-medusa-code-text-subtle-dark hover:!bg-medusa-code-bg-base dark:hover:!bg-medusa-code-bg-base-dark",
isCodeTabs &&
selectedValue === value &&
"text-medusa-code-text-base dark:text-medusa-code-text-base-dark border border-solid border-medusa-code-border dark:border-medusa-code-border-dark bg-medusa-code-bg-base dark:bg-medusa-code-bg-base-dark xs:!border-none xs:!bg-transparent",
!isCodeTabs &&
"border-0 border-b-[3px] rounded inline-flex p-1 transition-[background-color] duration-200 ease-ease",
!isCodeTabs &&
selectedValue === value &&
"border-solid border-medusa-fg-base dark:border-medusa-fg-base-dark rounded-b-none",
!isCodeTabs &&
selectedValue !== value &&
"text-medusa-fg-subtle dark:text-medusa-fg-subtle-dark",
(!isCodeTabs || !attributes?.badge) && "px-0.75",
isCodeTabs &&
attributes?.badge &&
"[&_.badge]:ml-0.5 [&_.badge]:py-0.125 [&_.badge]:px-[6px] [&_.badge]:rounded-full pl-0.75 pr-0.25"
)}
>
{label ?? value}
</li>
))}
</ul>
</div>
{isCodeTabs && (
<span
className={clsx(
"text-compact-small-plus text-medusa-code-text-subtle dark:text-medusa-code-text-subtle-dark hidden xs:block"
)}
>
{codeTitle}
</span>
)}
</div>
)
}
function TabContent({
lazy,
children,
selectedValue,
}: OldProps & ReturnType<typeof useTabs>) {
const childTabs = (Array.isArray(children) ? children : [children]).filter(
Boolean
) as ReactElement<TabItemProps>[]
if (lazy) {
const selectedTabItem = childTabs.find(
(tabItem) => tabItem.props.value === selectedValue
)
if (!selectedTabItem) {
// fail-safe or fail-fast? not sure what's best here
return null
}
return cloneElement(selectedTabItem)
}
return (
<div>
{childTabs.map((tabItem, i) =>
cloneElement(tabItem, {
key: i,
hidden: tabItem.props.value !== selectedValue,
})
)}
</div>
)
}
type TabsComponentProp = TabsCustomProps & OldProps
function TabsComponent(props: TabsComponentProp): JSX.Element {
const tabs = useTabs(props)
return (
<div className={clsx("mb-1.5")}>
<TabList {...props} {...tabs} />
<TabContent {...props} {...tabs} />
</div>
)
}
type TabsProps = {
wrapperClassName?: string
isCodeTabs?: boolean
} & OldProps
function checkCodeTabs(props: TabsProps): boolean {
return props.groupId === "npm2yarn" || props.isCodeTabs
}
export default function Tabs(props: TabsProps): JSX.Element {
const isBrowser = useIsBrowser()
useEffect(() => {
if (!window.localStorage.getItem("docusaurus.tab.npm2yarn")) {
// set the default
window.localStorage.setItem("docusaurus.tab.npm2yarn", "yarn")
}
}, [])
const isCodeTabs = checkCodeTabs(props)
return (
<div
className={clsx(
"tabs-wrapper",
props.wrapperClassName,
isCodeTabs && "code-tabs",
!isCodeTabs &&
"bg-docs-bg-surface dark:bg-docs-bg-surface-dark p-1 border border-solid border-medusa-border-base dark:border-medusa-border-base-dark rounded"
)}
>
<TabsComponent
// Remount tabs after hydration
// Temporary fix for https://github.com/facebook/docusaurus/issues/5653
key={String(isBrowser)}
isCodeTabs={isCodeTabs}
{...props}
/>
</div>
)
}