docs: redesign sidebar (#8408)

* initial changes

* redesign the sidebar + nav drawer

* changes to sidebar items

* finish up sidebar redesign

* support new sidebar in resources

* general fixes

* integrate in ui

* support api reference

* refactor

* integrate in user guide

* docs: fix build errors

* fix user guide build

* more refactoring

* added banner

* added bottom logo + icon

* fix up sidebar

* fix up paddings

* fix shadow bottom

* docs: add table of content (#8445)

* add toc types

* implement toc functionality

* finished toc redesign

* redesigned table of content

* mobile fixes

* truncate text in toc

* mobile fixes

* merge fixes

* implement redesign

* add hide sidebar

* add menu action item

* finish up hide sidebar design

* implement redesign in resources

* integrate in api reference

* integrate changes in ui

* fixes to api reference scrolling

* fix build error

* fix build errors

* fixes

* fixes to sidebar

* general fixes

* fix active category not closing

* fix long titles
This commit is contained in:
Shahed Nasser
2024-08-15 12:13:13 +03:00
committed by GitHub
parent 4cb28531e5
commit b4f3b8a79d
157 changed files with 5080 additions and 2010 deletions

View File

@@ -15,7 +15,7 @@ const ReferencePage = async () => {
</h1>
<DividedLayout
mainContent={
<Section>
<Section checkActiveOnScroll={true}>
<h1 className="!text-h2 hidden lg:block">
Medusa V2 Admin API Reference
</h1>

View File

@@ -1,5 +1,4 @@
import "./globals.css"
import Navbar from "@/components/Navbar"
import Providers from "../providers"
import { WideLayout } from "docs-ui"
import { Inter, Roboto_Mono } from "next/font/google"
@@ -29,8 +28,12 @@ export default function RootLayout({
return (
<WideLayout
ProvidersComponent={Providers}
NavbarComponent={Navbar}
sidebarProps={{
expandItems: false,
}}
bodyClassName={clsx(inter.variable, robotoMono.variable)}
showToc={false}
showBanner={false}
>
{children}
</WideLayout>

View File

@@ -15,7 +15,7 @@ const ReferencePage = async () => {
</h1>
<DividedLayout
mainContent={
<Section>
<Section checkActiveOnScroll={true}>
<h1 className="!text-h2 hidden lg:block">
Medusa V2 Store API Reference
</h1>

View File

@@ -1,73 +1,47 @@
"use client"
import { InView } from "react-intersection-observer"
import { isElmWindow, useScrollController, useSidebar } from "docs-ui"
import checkElementInViewport from "../../../utils/check-element-in-viewport"
import { useEffect, useMemo } from "react"
import { useScrollController, useSidebar } from "docs-ui"
import { useEffect, useMemo, useRef, useState } from "react"
import getSectionId from "../../../utils/get-section-id"
type H2Props = {
addToSidebar?: boolean
} & React.HTMLAttributes<HTMLHeadingElement>
type H2Props = React.HTMLAttributes<HTMLHeadingElement>
const H2 = ({ addToSidebar = true, children, ...props }: H2Props) => {
const { activePath, setActivePath, addItems } = useSidebar()
const { getScrolledTop, scrollableElement } = useScrollController()
const H2 = ({ children, ...props }: H2Props) => {
const headingRef = useRef<HTMLHeadingElement>(null)
const { activePath, addItems } = useSidebar()
const { scrollableElement, scrollToElement } = useScrollController()
const [scrolledFirstTime, setScrolledFirstTime] = useState(false)
const handleViewChange = (
inView: boolean,
entry: IntersectionObserverEntry
) => {
if (!addToSidebar) {
return
}
const heading = entry.target
if (
(inView ||
checkElementInViewport(heading.parentElement || heading, 40)) &&
getScrolledTop() !== 0 &&
activePath !== heading.id
) {
// can't use next router as it doesn't support
// changing url without scrolling
history.pushState({}, "", `#${heading.id}`)
setActivePath(heading.id)
}
}
const id = getSectionId([children as string])
const id = useMemo(() => getSectionId([children as string]), [children])
useEffect(() => {
if (id === (activePath || location.hash.replace("#", ""))) {
const elm = document.getElementById(id)
elm?.scrollIntoView()
if (!scrollableElement || !headingRef.current || scrolledFirstTime) {
return
}
if (id === (activePath || location.hash.replace("#", ""))) {
scrollToElement(
(headingRef.current.offsetParent as HTMLElement) || headingRef.current
)
}
setScrolledFirstTime(scrolledFirstTime)
}, [scrollableElement, headingRef, id])
useEffect(() => {
addItems([
{
type: "link",
path: `${id}`,
title: children as string,
loaded: true,
},
])
}, [])
const root = useMemo(() => {
return isElmWindow(scrollableElement) ? document.body : scrollableElement
}, [scrollableElement])
}, [id])
return (
<InView
as="h2"
threshold={0.4}
skip={!addToSidebar}
initialInView={false}
{...props}
onChange={handleViewChange}
id={id}
root={root}
>
<h2 {...props} id={id} ref={headingRef}>
{children}
</InView>
</h2>
)
}

View File

@@ -14,9 +14,7 @@ const getCustomComponents = (scope?: ScopeType): MDXComponents => {
...UiMDXComponents,
Security: () => <Security specs={scope?.specs} />,
a: Link,
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<H2 addToSidebar={scope?.addToSidebar} {...props} />
),
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => <H2 {...props} />,
}
}

View File

@@ -1,28 +0,0 @@
"use client"
import { Button, useModal, usePageLoading } from "docs-ui"
import DetailedFeedback from "../../DetailedFeedback"
const FeedbackModal = () => {
const { setModalProps } = useModal()
const { isLoading } = usePageLoading()
const openModal = () => {
if (isLoading) {
return
}
setModalProps({
title: "Send your Feedback",
children: <DetailedFeedback />,
contentClassName: "lg:!min-h-auto !p-0",
})
}
return (
<Button onClick={openModal} variant="secondary">
Feedback
</Button>
)
}
export default FeedbackModal

View File

@@ -1,50 +0,0 @@
"use client"
import {
Navbar as UiNavbar,
getNavbarItems,
usePageLoading,
useSidebar,
} from "docs-ui"
import FeedbackModal from "./FeedbackModal"
import { useMemo } from "react"
import { config } from "../../config"
import { usePathname } from "next/navigation"
import VersionSwitcher from "../VersionSwitcher"
import basePathUrl from "../../utils/base-path-url"
const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
const pathname = usePathname()
const { isLoading } = usePageLoading()
const navbarItems = useMemo(
() =>
getNavbarItems({
basePath: config.baseUrl,
activePath: basePathUrl(pathname),
version: "v2",
}),
[pathname]
)
return (
<UiNavbar
logo={{
light: basePathUrl("/images/logo-icon.png"),
dark: basePathUrl("/images/logo-icon-dark.png"),
}}
items={navbarItems}
mobileMenuButton={{
setMobileSidebarOpen,
mobileSidebarOpen,
}}
additionalActionsBefore={<VersionSwitcher />}
additionalActionsAfter={<FeedbackModal />}
showSearchOpener
isLoading={isLoading}
/>
)
}
export default Navbar

View File

@@ -1,14 +1,25 @@
"use client"
import clsx from "clsx"
import { useActiveOnScroll, useSidebar } from "docs-ui"
import { useEffect, useRef } from "react"
export type SectionProps = {
addToSidebar?: boolean
checkActiveOnScroll?: boolean
} & React.AllHTMLAttributes<HTMLDivElement>
const Section = ({ children, className }: SectionProps) => {
const Section = ({
children,
className,
checkActiveOnScroll = false,
}: SectionProps) => {
const sectionRef = useRef<HTMLDivElement>(null)
const { activeItemId } = useActiveOnScroll({
rootElm: sectionRef.current || undefined,
enable: checkActiveOnScroll,
useDefaultIfNoActive: false,
})
const { setActivePath } = useSidebar()
useEffect(() => {
if ("scrollRestoration" in history) {
@@ -17,6 +28,13 @@ const Section = ({ children, className }: SectionProps) => {
}
}, [])
useEffect(() => {
if (activeItemId.length) {
history.pushState({}, "", `#${activeItemId}`)
setActivePath(activeItemId)
}
}, [activeItemId])
return (
<div
ref={sectionRef}

View File

@@ -13,6 +13,7 @@ import TagsOperationDescriptionSection from "./DescriptionSection"
import DividedLayout from "@/layouts/Divided"
import { useLoading } from "@/providers/loading"
import SectionDivider from "../../Section/Divider"
import checkElementInViewport from "../../../utils/check-element-in-viewport"
const TagOperationCodeSection = dynamic<TagOperationCodeSectionProps>(
async () => import("./CodeSection")
@@ -40,7 +41,7 @@ const TagOperation = ({
)
const nodeRef = useRef<Element | null>(null)
const { loading, removeLoading } = useLoading()
const { scrollableElement } = useScrollController()
const { scrollableElement, scrollToTop } = useScrollController()
const root = useMemo(() => {
return isElmWindow(scrollableElement) ? document.body : scrollableElement
}, [scrollableElement])
@@ -75,24 +76,28 @@ const TagOperation = ({
[ref]
)
useEffect(() => {
const enableShow = () => {
setShow(true)
const scrollIntoView = useCallback(() => {
if (nodeRef.current && !checkElementInViewport(nodeRef.current, 10)) {
const elm = nodeRef.current as HTMLElement
scrollToTop(
elm.offsetTop + (elm.offsetParent as HTMLElement)?.offsetTop,
0
)
}
setShow(true)
}, [scrollToTop, nodeRef])
useEffect(() => {
if (nodeRef && nodeRef.current) {
removeLoading()
const currentHash = location.hash.replace("#", "")
if (currentHash === path) {
setTimeout(() => {
nodeRef.current?.scrollIntoView()
enableShow()
}, 100)
setTimeout(scrollIntoView, 100)
} else if (currentHash.split("_")[0] === path.split("_")[0]) {
enableShow()
setShow(true)
}
}
}, [nodeRef, path])
}, [nodeRef, path, scrollIntoView])
return (
<div

View File

@@ -14,7 +14,7 @@ import { useBaseSpecs } from "@/providers/base-specs"
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
import { useLoading } from "@/providers/loading"
import DividedLoading from "@/components/DividedLoading"
import { SidebarItemSections, SidebarItemType } from "types"
import { SidebarItemSections, SidebarItem, SidebarItemCategory } from "types"
import basePathUrl from "../../../utils/base-path-url"
const TagOperation = dynamic<TagOperationProps>(
@@ -56,16 +56,17 @@ const TagPaths = ({ tag, className }: TagPathsProps) => {
useEffect(() => {
if (paths) {
const parentItem = findItemInSection(
items[SidebarItemSections.BOTTOM],
{ path: tagSlugName },
items[SidebarItemSections.DEFAULT],
{ title: tag.name },
false
)
const pathItems: SidebarItemType[] = getTagChildSidebarItems(paths)
) as SidebarItemCategory
const pathItems: SidebarItem[] = getTagChildSidebarItems(paths)
if ((parentItem?.children?.length || 0) < pathItems.length) {
addItems(pathItems, {
section: SidebarItemSections.BOTTOM,
section: SidebarItemSections.DEFAULT,
parent: {
path: tagSlugName,
title: tag.name,
path: "",
changeLoaded: true,
},
})

View File

@@ -40,7 +40,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
},
})
const { scrollableElement } = useScrollController()
const { scrollableElement, scrollToElement } = useScrollController()
const root = useMemo(() => {
return isElmWindow(scrollableElement) ? document.body : scrollableElement
}, [scrollableElement])
@@ -49,6 +49,7 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
addItems(
[
{
type: "link",
path: schemaSlug,
title: `${formattedName} Object`,
additionalElms: <Badge variant="neutral">Schema</Badge>,
@@ -56,8 +57,9 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
},
],
{
section: SidebarItemSections.BOTTOM,
section: SidebarItemSections.DEFAULT,
parent: {
title: tagName,
path: tagSlugName,
changeLoaded: true,
},
@@ -69,12 +71,12 @@ const TagSectionSchema = ({ schema, tagName }: TagSectionSchemaProps) => {
useEffect(() => {
if (schemaSlug === (activePath || location.hash.replace("#", ""))) {
const elm = document.getElementById(schemaSlug)
elm?.scrollIntoView({
block: "center",
})
const elm = document.getElementById(schemaSlug) as HTMLElement
if (!checkElementInViewport(elm, 40)) {
scrollToElement(elm)
}
}
}, [])
}, [activePath, schemaSlug])
const handleViewChange = (
inView: boolean,

View File

@@ -26,6 +26,7 @@ import { SchemaObject, TagObject } from "@/types/openapi"
import useSWR from "swr"
import { TagSectionSchemaProps } from "./Schema"
import basePathUrl from "../../../utils/base-path-url"
import checkElementInViewport from "../../../utils/check-element-in-viewport"
export type TagSectionProps = {
tag: TagObject
@@ -52,7 +53,7 @@ const TagSection = ({ tag }: TagSectionProps) => {
const slugTagName = useMemo(() => getSectionId([tag.name]), [tag])
const { area } = useArea()
const pathname = usePathname()
const { scrollableElement } = useScrollController()
const { scrollableElement, scrollToElement } = useScrollController()
const { data } = useSWR<{
schema: SchemaObject
}>(
@@ -96,8 +97,10 @@ const TagSection = ({ tag }: TagSectionProps) => {
if (activePath && activePath.includes(slugTagName)) {
const tagName = activePath.split("_")
if (tagName.length === 1 && tagName[0] === slugTagName) {
const elm = document.getElementById(tagName[0]) as Element
elm?.scrollIntoView()
const elm = document.getElementById(tagName[0])
if (elm && !checkElementInViewport(elm, 10)) {
scrollToElement(elm)
}
} else if (tagName.length > 1 && tagName[0] === slugTagName) {
setLoadPaths(true)
}

View File

@@ -11,7 +11,7 @@ import { swrFetcher, useSidebar } from "docs-ui"
import getSectionId from "@/utils/get-section-id"
import { ExpandedDocument } from "@/types/openapi"
import getTagChildSidebarItems from "@/utils/get-tag-child-sidebar-items"
import { SidebarItemSections } from "types"
import { SidebarItem, SidebarItemSections } from "types"
import basePathUrl from "../../utils/base-path-url"
const TagSection = dynamic<TagSectionProps>(
@@ -31,7 +31,7 @@ const Tags = () => {
const [loadData, setLoadData] = useState<boolean>(false)
const [expand, setExpand] = useState<string>("")
const { baseSpecs, setBaseSpecs } = useBaseSpecs()
const { addItems } = useSidebar()
const { addItems, setActivePath } = useSidebar()
const { area, prevArea } = useArea()
const { data } = useSWR<ExpandedDocument>(
@@ -65,30 +65,42 @@ const Tags = () => {
if (baseSpecs) {
if (prevArea !== area) {
setBaseSpecs(null)
setLoadData(true)
return
}
addItems(
baseSpecs.tags?.map((tag) => {
const itemsToAdd: SidebarItem[] = [
{
type: "separator",
},
]
if (baseSpecs.tags) {
baseSpecs.tags.forEach((tag) => {
const tagPathName = getSectionId([tag.name.toLowerCase()])
const childItems =
baseSpecs.expandedTags &&
Object.hasOwn(baseSpecs.expandedTags, tagPathName)
? getTagChildSidebarItems(baseSpecs.expandedTags[tagPathName])
: []
return {
path: tagPathName,
itemsToAdd.push({
type: "category",
title: tag.name,
children: childItems,
loaded: childItems.length > 0,
}
}) || [],
{
section: SidebarItemSections.BOTTOM,
}
)
onOpen: () => {
history.pushState({}, "", `#${tagPathName}`)
setActivePath(tagPathName)
},
})
})
}
addItems(itemsToAdd, {
section: SidebarItemSections.DEFAULT,
})
}
}, [baseSpecs, addItems])
}, [baseSpecs, prevArea, area])
return (
<>

View File

@@ -8,14 +8,14 @@ export const config: DocsConfig = {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
// sidebar is auto generated
sidebar: {
top: [
default: [
{
type: "link",
title: "Introduction",
path: "",
loaded: true,
},
],
bottom: [],
mobile: getMobileSidebarItems({
baseUrl,
version: "v2",

View File

@@ -13,6 +13,7 @@ import BaseSpecsProvider from "./base-specs"
import SidebarProvider from "./sidebar"
import SearchProvider from "./search"
import { config } from "../config"
import { MainNavProvider } from "./main-nav"
type ProvidersProps = {
children?: React.ReactNode
@@ -28,9 +29,11 @@ const Providers = ({ children }: ProvidersProps) => {
<BaseSpecsProvider>
<ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider>
<SearchProvider>
<MobileProvider>{children}</MobileProvider>
</SearchProvider>
<MainNavProvider>
<SearchProvider>
<MobileProvider>{children}</MobileProvider>
</SearchProvider>
</MainNavProvider>
</SidebarProvider>
</ScrollControllerProvider>
</BaseSpecsProvider>

View File

@@ -0,0 +1,48 @@
"use client"
import {
formatReportLink,
getNavDropdownItems,
MainNavProvider as UiMainNavProvider,
useIsBrowser,
} from "docs-ui"
import { useMemo } from "react"
import { config } from "../config"
import { usePathname } from "next/navigation"
import basePathUrl from "../utils/base-path-url"
type MainNavProviderProps = {
children?: React.ReactNode
}
export const MainNavProvider = ({ children }: MainNavProviderProps) => {
const isBrowser = useIsBrowser()
const pathname = usePathname()
const navigationDropdownItems = useMemo(
() =>
getNavDropdownItems({
basePath: config.baseUrl,
activePath: basePathUrl(pathname),
version: "v2",
}),
[pathname]
)
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return (
<UiMainNavProvider
navItems={navigationDropdownItems}
reportIssueLink={reportLink}
>
{children}
</UiMainNavProvider>
)
}

View File

@@ -3,6 +3,7 @@
import { createContext, useEffect } from "react"
import { capitalize, useSidebar } from "docs-ui"
import { useArea } from "./area"
import { SidebarItemLink } from "types"
const PageTitleContext = createContext(null)
@@ -25,7 +26,9 @@ const PageTitleProvider = ({ children }: PageTitleProviderProps) => {
document.title = `${activeItem?.title} - ${titleSuffix}`
} else {
// find the child that matches the active path
const item = activeItem?.children?.find((i) => i.path === activePath)
const item = activeItem?.children?.find(
(i) => i.type === "link" && i.path === activePath
) as SidebarItemLink
if (item) {
document.title = `${item.title} - ${titleSuffix}`
}

View File

@@ -1,4 +1,5 @@
"use client"
import {
SidebarProvider as UiSidebarProvider,
usePageLoading,
@@ -6,8 +7,8 @@ import {
useScrollController,
} from "docs-ui"
import { config } from "../config"
import { usePathname } from "next/navigation"
import { useCallback } from "react"
import { usePathname } from "next/navigation"
type SidebarProviderProps = {
children?: React.ReactNode
@@ -17,11 +18,11 @@ const SidebarProvider = ({ children }: SidebarProviderProps) => {
const { isLoading, setIsLoading } = usePageLoading()
const { scrollableElement } = useScrollController()
const pathname = usePathname()
const prevPathname = usePrevious(pathname)
const prevPathName = usePrevious(pathname)
const resetOnCondition = useCallback(
() => prevPathname !== undefined && prevPathname !== pathname,
[pathname, prevPathname]
() => prevPathName !== undefined && pathname !== prevPathName,
[pathname, prevPathName]
)
return (

View File

@@ -1,5 +1,5 @@
import { getLinkWithBasePath } from "docs-ui"
export default function basePathUrl(path: string) {
export default function basePathUrl(path = "") {
return getLinkWithBasePath(path, process.env.NEXT_PUBLIC_BASE_PATH)
}

View File

@@ -4,7 +4,7 @@ export default function checkElementInViewport(
height?: number
) {
const rect = element.getBoundingClientRect()
const windowHeight =
const windowHeight: number | undefined =
height || window.innerHeight || document.documentElement.clientHeight
return !(

View File

@@ -1,4 +1,4 @@
import type { SidebarItemType } from "types"
import type { SidebarItem } from "types"
import type { Operation, PathsObject } from "@/types/openapi"
import type { OpenAPIV3 } from "openapi-types"
import dynamic from "next/dynamic"
@@ -11,13 +11,14 @@ const MethodLabel = dynamic<MethodLabelProps>(
export default function getTagChildSidebarItems(
paths: PathsObject
): SidebarItemType[] {
const items: SidebarItemType[] = []
): SidebarItem[] {
const items: SidebarItem[] = []
Object.entries(paths).forEach(([, operations]) => {
Object.entries(operations).map(([method, operation]) => {
const definedOperation = operation as Operation
const definedMethod = method as OpenAPIV3.HttpMethods
items.push({
type: "link",
path: getSectionId([
...(definedOperation.tags || []),
definedOperation.operationId,

View File

@@ -1,10 +1,8 @@
import type { Metadata } from "next"
import { Inter, Roboto_Mono } from "next/font/google"
import Navbar from "@/components/Navbar"
import Providers from "@/providers"
import "./globals.css"
import { Bannerv2, TightLayout } from "docs-ui"
import { TightLayout } from "docs-ui"
import { config } from "@/config"
import clsx from "clsx"
import Feedback from "@/components/Feedback"
@@ -40,10 +38,8 @@ export default function RootLayout({
return (
<TightLayout
ProvidersComponent={Providers}
NavbarComponent={Navbar}
sidebarProps={{
expandItems: true,
banner: <Bannerv2 />,
}}
showPagination={true}
bodyClassName={clsx(inter.variable, robotoMono.variable)}

View File

@@ -1,39 +0,0 @@
"use client"
import { Navbar as UiNavbar, getNavbarItems } from "docs-ui"
import { useSidebar } from "docs-ui"
import { useMemo } from "react"
import { config } from "../../config"
import { basePathUrl } from "../../utils/base-path-url"
const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
const navbarItems = useMemo(
() =>
getNavbarItems({
basePath: config.baseUrl,
activePath: basePathUrl(),
version: "v2",
}),
[]
)
return (
<UiNavbar
logo={{
light: basePathUrl("/images/logo-icon.png"),
dark: basePathUrl("/images/logo-icon-dark.png"),
}}
items={navbarItems}
mobileMenuButton={{
setMobileSidebarOpen,
mobileSidebarOpen,
}}
isLoading={false}
showSearchOpener
/>
)
}
export default Navbar

View File

@@ -1,17 +1,20 @@
import { Badge, getMobileSidebarItems } from "docs-ui"
import type { SidebarConfig, SidebarItemType } from "@/types"
import type { SidebarConfig, SidebarItem } from "@/types"
import { sidebar } from "../sidebar.mjs"
const soonBadge = <Badge variant="blue">Soon</Badge>
const normalizeSidebarItems = (items: SidebarItemType[]) =>
const normalizeSidebarItems = (items: SidebarItem[]) =>
items.map((item) => {
if (item.type === "separator") {
return item
}
if (item.isSoon) {
item.additionalElms = soonBadge
}
if (item.children) {
item.children = normalizeSidebarItems(item.children as SidebarItemType[])
item.children = normalizeSidebarItems(item.children as SidebarItem[])
}
return item
@@ -19,8 +22,7 @@ const normalizeSidebarItems = (items: SidebarItemType[]) =>
export const sidebarConfig = (baseUrl: string): SidebarConfig => {
return {
top: normalizeSidebarItems(sidebar),
bottom: [],
default: normalizeSidebarItems(sidebar),
mobile: getMobileSidebarItems({
baseUrl,
version: "v2",

View File

@@ -16,6 +16,7 @@ import {
import SidebarProvider from "./sidebar"
import SearchProvider from "./search"
import { config } from "../config"
import { MainNavProvider } from "./main-nav"
type ProvidersProps = {
children?: React.ReactNode
@@ -33,16 +34,18 @@ const Providers = ({ children }: ProvidersProps) => {
<ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider>
<PaginationProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
currentLearningPath: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
<MainNavProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
currentLearningPath: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
</MainNavProvider>
</PaginationProvider>
</SidebarProvider>
</ScrollControllerProvider>

View File

@@ -0,0 +1,46 @@
"use client"
import {
formatReportLink,
getNavDropdownItems,
MainNavProvider as UiMainNavProvider,
useIsBrowser,
} from "docs-ui"
import { useMemo } from "react"
import { config } from "../config"
import { basePathUrl } from "../utils/base-path-url"
type MainNavProviderProps = {
children?: React.ReactNode
}
export const MainNavProvider = ({ children }: MainNavProviderProps) => {
const isBrowser = useIsBrowser()
const navigationDropdownItems = useMemo(
() =>
getNavDropdownItems({
basePath: config.baseUrl,
activePath: basePathUrl(),
version: "v2",
}),
[]
)
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return (
<UiMainNavProvider
navItems={navigationDropdownItems}
reportIssueLink={reportLink}
>
{children}
</UiMainNavProvider>
)
}

View File

@@ -1,4 +1,5 @@
"use client"
import {
SidebarProvider as UiSidebarProvider,
useScrollController,
@@ -20,7 +21,6 @@ const SidebarProvider = ({ children }: SidebarProviderProps) => {
initialItems={config.sidebar}
staticSidebarItems={true}
disableActiveTransition={true}
noTitleStyling={true}
>
{children}
</UiSidebarProvider>

View File

@@ -1,183 +1,226 @@
import numberSidebarItems from "./utils/number-sidebar-items.mjs"
import { sidebarAttachHrefCommonOptions } from "./utils/sidebar-attach-common-options.mjs"
/** @type {import('@/types').SidebarItemType[]} */
export const sidebar = sidebarAttachHrefCommonOptions(
numberSidebarItems([
/** @type {import('@/types').SidebarItem[]} */
export const sidebar = numberSidebarItems(
sidebarAttachHrefCommonOptions([
{
type: "link",
path: "/",
title: "Introduction",
},
{
type: "link",
path: "/first-customizations",
title: "Your First Customizations",
},
{
type: "link",
path: "/basics",
title: "The Basics",
children: [
{
type: "link",
path: "/basics/project-directories-files",
title: "Project Directories and Files",
},
{
type: "link",
path: "/basics/medusa-container",
title: "Medusa Container",
},
{
type: "link",
path: "/basics/api-routes",
title: "API Routes",
},
{
type: "link",
path: "/basics/modules-and-services",
title: "Modules and Services",
},
{
type: "link",
path: "/basics/commerce-modules",
title: "Commerce Modules",
},
{
type: "link",
path: "/basics/modules-directory-structure",
title: "Modules Directory Structure",
},
{
type: "link",
path: "/basics/data-models",
title: "Data Models",
},
{
type: "link",
path: "/basics/loaders",
title: "Loaders",
},
{
type: "link",
path: "/basics/events-and-subscribers",
title: "Events and Subscribers",
},
{
type: "link",
path: "/basics/scheduled-jobs",
title: "Scheduled Jobs",
},
{
type: "link",
path: "/basics/workflows",
title: "Workflows",
},
{
type: "link",
path: "/basics/admin-customizations",
title: "Admin Customizations",
},
],
},
{
type: "link",
path: "/advanced-development",
title: "Advanced Development",
children: [
{
type: "sub-category",
title: "API Routes",
children: [
{
type: "link",
path: "/advanced-development/api-routes/http-methods",
title: "HTTP Methods",
},
{
type: "link",
path: "/advanced-development/api-routes/parameters",
title: "Parameters",
},
{
type: "link",
path: "/advanced-development/api-routes/middlewares",
title: "Middlewares",
},
{
type: "link",
path: "/advanced-development/api-routes/protected-routes",
title: "Protected Routes",
},
{
type: "link",
path: "/advanced-development/api-routes/cors",
title: "Handling CORS",
},
],
},
{
type: "sub-category",
title: "Modules",
children: [
{
type: "link",
path: "/advanced-development/modules/container",
title: "Module's Container",
},
{
type: "link",
path: "/advanced-development/modules/service-factory",
title: "Service Factory",
},
{
type: "link",
path: "/advanced-development/modules/isolation",
title: "Module Isolation",
},
{
type: "link",
path: "/advanced-development/modules/module-links",
title: "Module Links",
},
{
type: "link",
path: "/advanced-development/modules/module-link-directions",
title: "Module Link Direction",
},
{
type: "link",
path: "/advanced-development/modules/remote-link",
title: "Remote Link",
},
{
type: "link",
path: "/advanced-development/modules/remote-query",
title: "Remote Query",
},
{
type: "link",
path: "/advanced-development/modules/options",
title: "Module Options",
},
],
},
{
type: "link",
path: "/advanced-development/data-models",
title: "Data Models",
children: [
{
type: "link",
path: "/advanced-development/data-models/property-types",
title: "Property Types",
},
{
type: "link",
path: "/advanced-development/data-models/primary-key",
title: "Primary Key",
},
{
type: "link",
path: "/advanced-development/data-models/default-properties",
title: "Default Properties",
},
{
type: "link",
path: "/advanced-development/data-models/configure-properties",
title: "Configure Properties",
},
{
type: "link",
path: "/advanced-development/data-models/relationships",
title: "Relationships",
},
{
type: "link",
path: "/advanced-development/data-models/manage-relationships",
title: "Manage Relationships",
},
{
type: "link",
path: "/advanced-development/data-models/index",
title: "Index",
},
{
type: "link",
path: "/advanced-development/data-models/searchable-property",
title: "Searchable Property",
},
{
type: "link",
path: "/advanced-development/data-models/write-migration",
title: "Write Migration",
},
],
},
{
type: "sub-category",
title: "Events and Subscribers",
children: [
{
type: "link",
path: "/advanced-development/events-and-subscribers/data-payload",
title: "Events Data Payload",
},
@@ -188,84 +231,104 @@ export const sidebar = sidebarAttachHrefCommonOptions(
],
},
{
type: "sub-category",
title: "Scheduled Jobs",
children: [
{
type: "link",
path: "/advanced-development/scheduled-jobs/execution-number",
title: "Execution Number",
},
],
},
{
type: "sub-category",
title: "Workflows",
children: [
{
type: "link",
path: "/advanced-development/workflows/constructor-constraints",
title: "Workflow Constraints",
},
{
type: "link",
path: "/advanced-development/workflows/conditions",
title: "Conditions in Workflows",
},
{
type: "link",
path: "/advanced-development/workflows/compensation-function",
title: "Compensation Function",
},
{
type: "link",
path: "/advanced-development/workflows/workflow-hooks",
title: "Workflow Hooks",
},
{
type: "link",
path: "/advanced-development/workflows/add-workflow-hook",
title: "Expose a Hook",
},
{
type: "link",
path: "/advanced-development/workflows/access-workflow-errors",
title: "Access Workflow Errors",
},
{
type: "link",
path: "/advanced-development/workflows/retry-failed-steps",
title: "Retry Failed Steps",
},
{
type: "link",
path: "/advanced-development/workflows/parallel-steps",
title: "Run Steps in Parallel",
},
{
type: "link",
path: "/advanced-development/workflows/workflow-timeout",
title: "Workflow Timeout",
},
{
type: "link",
path: "/advanced-development/workflows/long-running-workflow",
title: "Long-Running Workflow",
},
{
type: "link",
path: "/advanced-development/workflows/execute-another-workflow",
title: "Execute Another Workflow",
},
{
type: "link",
path: "/advanced-development/workflows/advanced-example",
title: "Example: Advanced Workflow",
},
],
},
{
type: "link",
path: "/advanced-development/custom-cli-scripts",
title: "Custom CLI Scripts",
},
{
type: "link",
path: "/advanced-development/admin",
title: "Admin Development",
children: [
{
type: "link",
path: "/advanced-development/admin/widgets",
title: "Admin Widgets",
},
{
type: "link",
path: "/advanced-development/admin/ui-routes",
title: "Admin UI Routes",
},
{
type: "link",
path: "/advanced-development/admin/tips",
title: "Tips",
},
@@ -274,42 +337,51 @@ export const sidebar = sidebarAttachHrefCommonOptions(
],
},
{
type: "link",
path: "/storefront-development",
title: "Storefront Development",
children: [
{
type: "link",
path: "/storefront-development/nextjs-starter",
title: "Next.js Starter",
},
],
},
{
type: "link",
path: "/architectural-modules",
title: "Architectural Modules",
},
{
type: "link",
path: "/debugging-and-testing",
title: "Debugging and Testing",
children: [
{
type: "link",
path: "/debugging-and-testing/logging",
title: "Logging",
},
{
type: "link",
path: "/debugging-and-testing/tools",
title: "Tools",
},
],
},
{
type: "link",
path: "/deployment",
title: "Deployment",
},
{
type: "link",
path: "/more-resources",
title: "More Resources",
},
{
type: "link",
path: "/cheatsheet",
title: "Cheat Sheet",
},

View File

@@ -1,11 +1,8 @@
import {
SidebarSectionItemsType,
SidebarItemType as UiSidebarItemType,
} from "docs-ui"
import { SidebarSectionItems, SidebarItem as SidebarItemType } from "types"
export declare type SidebarItemType = UiSidebarItemType & {
export declare type SidebarItem = SidebarItemType & {
isSoon: boolean
number?: string
}
export declare type SidebarConfig = SidebarSectionItemsType
export declare type SidebarConfig = SidebarSectionItems

View File

@@ -1,14 +1,34 @@
/**
*
* @param {import("@/types").SidebarItemType[]} sidebarItems - The items to add numbers to their titles
* @param {import("@/types").SidebarItem[]} sidebarItems - The items to add numbers to their titles
* @param {number[]} numbering - The current numbering level
* @returns {import("@/types").SidebarItemType[]} The modified sidebar items
* @returns {import("@/types").SidebarItem[]} The modified sidebar items
*/
export default function numberSidebarItems(sidebarItems, numbering = [1]) {
// TODO generate chapter titles
if (!numbering.length) {
numbering.push(1)
}
const isTopItems = numbering.length === 1
/** @type {import("@/types").SidebarItem[]} */
const numberedItems = []
/** @type {import("@/types").SidebarItem | undefined} */
let parentItem
sidebarItems.forEach((item) => {
if (item.type === "separator") {
;(parentItem?.children || numberedItems).push(item)
}
if (isTopItems) {
// Add chapter category
numberedItems.push({
type: "category",
title: `Chapter ${padNumber(numbering[0])}`,
children: [],
loaded: true,
})
parentItem = numberedItems[numberedItems.length - 1]
}
// append current number to the item's title
item.number = `${numbering.join(".")}.`
item.title = `${item.number} ${item.title.trim()}`
@@ -17,8 +37,19 @@ export default function numberSidebarItems(sidebarItems, numbering = [1]) {
item.children = numberSidebarItems(item.children, [...numbering, 1])
}
;(parentItem?.children || numberedItems).push(item)
numbering[numbering.length - 1]++
})
return sidebarItems
return numberedItems
}
function padNumber(number) {
number = number.toString()
if (number.length < 2) {
number = `0` + number
}
return number
}

View File

@@ -1,4 +1,4 @@
/** @type {Partial<import("../providers").SidebarItemType>} */
/** @type {Partial<import("@/types").SidebarItem[]>} */
const commonOptions = {
loaded: true,
isPathHref: true,
@@ -6,13 +6,26 @@ const commonOptions = {
/**
*
* @param {import("../providers").SidebarItemType[]} sidebar
* @returns {import("../providers").SidebarItemType[]}
* @param {import("@/types").SidebarItem[]} sidebar
* @param {boolean} nested
* @returns {import("@/types").SidebarItem[]}
*/
export function sidebarAttachHrefCommonOptions(sidebar) {
return sidebar.map((item) => ({
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || []),
}))
export function sidebarAttachHrefCommonOptions(sidebar, nested = false) {
return sidebar.map((item) => {
if (item.type === "separator") {
return item
}
const updatedItem = {
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || [], true),
}
if (updatedItem.type !== "category" && !nested) {
updatedItem.childrenSameLevel = true
}
return updatedItem
})
}

View File

@@ -1,10 +1,8 @@
import type { Metadata } from "next"
import { Inter, Roboto_Mono } from "next/font/google"
import Navbar from "@/components/Navbar"
import Providers from "@/providers"
import "./globals.css"
import { Bannerv2, Breadcrumbs, TightLayout } from "docs-ui"
import { Breadcrumbs, TightLayout } from "docs-ui"
import { config } from "@/config"
import clsx from "clsx"
import { Feedback } from "@/components/Feedback"
@@ -41,14 +39,11 @@ export default function RootLayout({
return (
<TightLayout
ProvidersComponent={Providers}
NavbarComponent={Navbar}
sidebarProps={{
expandItems: true,
banner: <Bannerv2 />,
}}
bodyClassName={clsx(inter.variable, robotoMono.variable)}
>
<Breadcrumbs />
{children}
<Feedback className="my-2" />
<EditButton />

View File

@@ -10,4 +10,4 @@ This section of the documentation provides a reference to the workflows created
You can use these workflows in your customizations as well. They're available in the `@medusajs/core-flows` package.
<ChildDocs childLevel={2} />
<ChildDocs childLevel={2} hideItems={["Overview"]} />

View File

@@ -1,38 +0,0 @@
"use client"
import { Navbar as UiNavbar, getNavbarItems } from "docs-ui"
import { useSidebar } from "docs-ui"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
import { config } from "../../config"
const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
const navbarItems = useMemo(
() =>
getNavbarItems({
basePath: config.baseUrl,
activePath: basePathUrl(""),
version: "v2",
}),
[]
)
return (
<UiNavbar
logo={{
light: basePathUrl("/images/logo-icon.png"),
dark: basePathUrl("/images/logo-icon-dark.png"),
}}
items={navbarItems}
mobileMenuButton={{
setMobileSidebarOpen,
mobileSidebarOpen,
}}
isLoading={false}
showSearchOpener
/>
)
}
export default Navbar

View File

@@ -1,4 +1,4 @@
import { DocsConfig } from "types"
import { DocsConfig, SidebarItem } from "types"
import { getMobileSidebarItems } from "docs-ui"
import { generatedSidebar } from "../generated/sidebar.mjs"
@@ -9,8 +9,7 @@ export const config: DocsConfig = {
baseUrl,
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
sidebar: {
top: generatedSidebar,
bottom: [],
default: generatedSidebar as SidebarItem[],
mobile: getMobileSidebarItems({
baseUrl,
version: "v2",

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ import {
import SidebarProvider from "./sidebar"
import SearchProvider from "./search"
import { config } from "../config"
import { MainNavProvider } from "./main-nav"
type ProvidersProps = {
children?: React.ReactNode
@@ -35,16 +36,18 @@ const Providers = ({ children }: ProvidersProps) => {
<ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider>
<PaginationProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
currentLearningPath: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
<MainNavProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
currentLearningPath: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
</MainNavProvider>
</PaginationProvider>
</SidebarProvider>
</ScrollControllerProvider>

View File

@@ -0,0 +1,46 @@
"use client"
import {
formatReportLink,
getNavDropdownItems,
MainNavProvider as UiMainNavProvider,
useIsBrowser,
} from "docs-ui"
import { useMemo } from "react"
import { config } from "../config"
import { basePathUrl } from "../utils/base-path-url"
type MainNavProviderProps = {
children?: React.ReactNode
}
export const MainNavProvider = ({ children }: MainNavProviderProps) => {
const isBrowser = useIsBrowser()
const navigationDropdownItems = useMemo(
() =>
getNavDropdownItems({
basePath: config.baseUrl,
activePath: basePathUrl(),
version: "v2",
}),
[]
)
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return (
<UiMainNavProvider
navItems={navigationDropdownItems}
reportIssueLink={reportLink}
>
{children}
</UiMainNavProvider>
)
}

View File

@@ -20,7 +20,6 @@ const SidebarProvider = ({ children }: SidebarProviderProps) => {
initialItems={config.sidebar}
staticSidebarItems={true}
disableActiveTransition={true}
noTitleStyling={true}
>
{children}
</UiSidebarProvider>

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/** @type {Partial<import("../types").SidebarItemType>} */
/** @type {Partial<import("../types").RawSidebarItem>} */
const commonOptions = {
loaded: true,
isPathHref: true,
@@ -6,13 +6,19 @@ const commonOptions = {
/**
*
* @param {import("types").SidebarItemType[]} sidebar
* @returns {import("types").SidebarItemType[]}
* @param {import("types").RawSidebarItem[]} sidebar
* @returns {import("types").RawSidebarItem[]}
*/
export function sidebarAttachHrefCommonOptions(sidebar) {
return sidebar.map((item) => ({
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || []),
}))
return sidebar.map((item) => {
if (item.type === "separator") {
return item
}
return {
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || []),
}
})
}

View File

@@ -1,5 +1,6 @@
import { defineDocumentType, makeSource } from "contentlayer/source-files"
import { rehypeComponent } from "./src/lib/rehype-component"
import rehypeSlug from "rehype-slug"
export const Doc = defineDocumentType(() => ({
name: "Doc",
@@ -26,6 +27,6 @@ export default makeSource({
contentDirPath: "./src/content",
documentTypes: [Doc],
mdx: {
rehypePlugins: [rehypeComponent],
rehypePlugins: [[rehypeComponent], [rehypeSlug]],
},
})

View File

@@ -37,6 +37,7 @@
"react": "18.2.0",
"react-day-picker": "^8.10.0",
"react-dom": "18.2.0",
"rehype-slug": "^6.0.0",
"remark": "^14.0.3",
"tailwind": "*",
"tailwindcss": "3.3.3",

View File

@@ -2,7 +2,7 @@ import { MetadataRoute } from "next"
import { docsConfig } from "@/config/docs"
import { absoluteUrl } from "@/lib/absolute-url"
import { SidebarItemType } from "types"
import { SidebarItem } from "types"
export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date()
@@ -12,23 +12,24 @@ export default function sitemap(): MetadataRoute.Sitemap {
lastModified?: string | Date
}> = []
function pushItems(newItems: SidebarItemType[]) {
function pushItems(newItems: SidebarItem[]) {
newItems.forEach((item) => {
if (item.path) {
items.push({
url: absoluteUrl(item.path),
lastModified: now,
})
if (item.type !== "link") {
return
}
items.push({
url: absoluteUrl(item.path),
lastModified: now,
})
if (item.children) {
pushItems(item.children)
}
})
}
pushItems(docsConfig.sidebar.top)
pushItems(docsConfig.sidebar.bottom)
pushItems(docsConfig.sidebar.default)
return items
}

View File

@@ -1,6 +1,5 @@
import type { Metadata } from "next"
import { Navbar } from "@/components/navbar"
import { Providers } from "@/providers"
import { siteConfig } from "@/config/site"
@@ -36,11 +35,11 @@ export default function RootLayout({
return (
<TightLayout
ProvidersComponent={Providers}
NavbarComponent={Navbar}
sidebarProps={{
expandItems: true,
}}
bodyClassName={clsx(inter.variable, robotoMono.variable)}
showBanner={false}
>
{children}
</TightLayout>

View File

@@ -1,25 +0,0 @@
"use client"
import { docsConfig } from "@/config/docs"
import { basePathUrl } from "@/lib/base-path-url"
import { Navbar as UiNavbar, useSidebar } from "docs-ui"
const Navbar = () => {
const { mobileSidebarOpen, setMobileSidebarOpen } = useSidebar()
return (
<UiNavbar
logo={{
light: basePathUrl("/images/logo-icon.png"),
dark: basePathUrl("/images/logo-icon-dark.png"),
}}
items={docsConfig.mainNav}
mobileMenuButton={{
setMobileSidebarOpen,
mobileSidebarOpen,
}}
/>
)
}
export { Navbar }

View File

@@ -1,281 +1,360 @@
import { ArrowUpRightOnBox } from "@medusajs/icons"
import { NavbarItem, getMobileSidebarItems, getNavbarItems } from "docs-ui"
import { SidebarSectionItemsType } from "types"
import { getMobileSidebarItems } from "docs-ui"
import { SidebarSectionItems } from "types"
import { siteConfig } from "./site"
type DocsConfig = {
mainNav: NavbarItem[]
sidebar: SidebarSectionItemsType
sidebar: SidebarSectionItems
}
export const docsConfig: DocsConfig = {
mainNav: getNavbarItems({
basePath: siteConfig.baseUrl,
activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/ui",
version: "v1",
}),
sidebar: {
top: [
default: [
{
title: "Getting Started",
children: [
{
title: "Introduction",
path: "/",
loaded: true,
isPathHref: true,
},
],
type: "link",
title: "Introduction",
path: "/",
loaded: true,
isPathHref: true,
},
{
type: "separator",
},
{
type: "category",
title: "Installation",
loaded: true,
children: [
{
type: "link",
title: "Medusa Admin Extension",
path: "/installation/medusa-admin-extension",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Standalone Project",
path: "/installation/standalone-project",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Upgrade Guides",
path: `${process.env.NEXT_PUBLIC_DOCS_URL}/upgrade-guides/medusa-ui`,
isPathHref: true,
additionalElms: <ArrowUpRightOnBox />,
linkProps: {
target: "_blank",
rel: "noreferrer",
},
loaded: true,
},
],
},
],
bottom: [
{
type: "category",
title: "Colors",
loaded: true,
children: [
{
type: "link",
title: "Overview",
path: "/colors/overview",
isPathHref: true,
loaded: true,
},
],
},
{
type: "link",
title: "Icons",
children: [
{
title: "Overview",
path: "/icons/overview",
isPathHref: true,
},
],
path: "/icons/overview",
isPathHref: true,
loaded: true,
},
{
type: "separator",
},
{
type: "category",
title: "Components",
loaded: true,
children: [
{
type: "link",
title: "Alert",
path: "/components/alert",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Avatar",
path: "/components/avatar",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Badge",
path: "/components/badge",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Button",
path: "/components/button",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Calendar",
path: "/components/calendar",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Checkbox",
path: "/components/checkbox",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Code Block",
path: "/components/code-block",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Command",
path: "/components/command",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Command Bar",
path: "/components/command-bar",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Container",
path: "/components/container",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Copy",
path: "/components/copy",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Currency Input",
path: "/components/currency-input",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Date Picker",
path: "/components/date-picker",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Drawer",
path: "/components/drawer",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Dropdown Menu",
path: "/components/dropdown-menu",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Focus Modal",
path: "/components/focus-modal",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Heading",
path: "/components/heading",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Icon Badge",
path: "/components/icon-badge",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Icon Button",
path: "/components/icon-button",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Input",
path: "/components/input",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Kbd",
path: "/components/kbd",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Label",
path: "/components/label",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Progress Accordion",
path: "/components/progress-accordion",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Progress Tabs",
path: "/components/progress-tabs",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Prompt",
path: "/components/prompt",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Radio Group",
path: "/components/radio-group",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Select",
path: "/components/select",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Status Badge",
path: "/components/status-badge",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Switch",
path: "/components/switch",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Table",
path: "/components/table",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Tabs",
path: "/components/tabs",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Text",
path: "/components/text",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Textarea",
path: "/components/textarea",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Toast",
path: "/components/toast",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "Tooltip",
path: "/components/tooltip",
isPathHref: true,
loaded: true,
},
],
},
{
type: "category",
title: "Hooks",
loaded: true,
children: [
{
type: "link",
title: "usePrompt",
path: "/hooks/use-prompt",
isPathHref: true,
loaded: true,
},
{
type: "link",
title: "useToggleState",
path: "/hooks/use-toggle-state",
isPathHref: true,
loaded: true,
},
],
},
{
type: "category",
title: "Utils",
loaded: true,
children: [
{
type: "link",
title: "clx",
path: "/utils/clx",
isPathHref: true,
loaded: true,
},
],
},

View File

@@ -16,8 +16,7 @@ export const siteConfig: SiteConfig = {
description: "Primitives for building Medusa applications.",
// sidebar is defined in docs.tsx
sidebar: {
top: [],
bottom: [],
default: [],
mobile: [],
},
}

View File

@@ -11,6 +11,7 @@ import {
import SearchProvider from "./search"
import SidebarProvider from "./sidebar"
import { siteConfig } from "../config/site"
import { MainNavProvider } from "./main-nav"
type ProvidersProps = {
children: React.ReactNode
@@ -25,7 +26,9 @@ const Providers = ({ children }: ProvidersProps) => {
<ModalProvider>
<ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider>
<SearchProvider>{children}</SearchProvider>
<MainNavProvider>
<SearchProvider>{children}</SearchProvider>
</MainNavProvider>
</SidebarProvider>
</ScrollControllerProvider>
</ModalProvider>

View File

@@ -0,0 +1,42 @@
"use client"
import {
formatReportLink,
getNavDropdownItems,
MainNavProvider as UiMainNavProvider,
useIsBrowser,
} from "docs-ui"
import { useMemo } from "react"
import { siteConfig } from "../config/site"
import { basePathUrl } from "../lib/base-path-url"
type MainNavProviderProps = {
children?: React.ReactNode
}
export const MainNavProvider = ({ children }: MainNavProviderProps) => {
const isBrowser = useIsBrowser()
const navigationDropdownItems = useMemo(
() =>
getNavDropdownItems({
basePath: siteConfig.baseUrl,
activePath: basePathUrl(),
version: "v1",
}),
[]
)
const reportLink = useMemo(
() => formatReportLink("UI Docs", isBrowser ? document.title : "", "ui"),
[isBrowser]
)
return (
<UiMainNavProvider
navItems={navigationDropdownItems}
reportIssueLink={reportLink}
>
{children}
</UiMainNavProvider>
)
}

View File

@@ -10,6 +10,7 @@ type SidebarProviderProps = {
const SidebarProvider = ({ children }: SidebarProviderProps) => {
const { scrollableElement } = useScrollController()
return (
<UiSidebarProvider
initialItems={docsConfig.sidebar}

View File

@@ -1,7 +1,5 @@
import type { Metadata } from "next"
import { Inter, Roboto_Mono } from "next/font/google"
import Navbar from "@/components/Navbar"
import Providers from "@/providers"
import "../css/globals.css"
import { TightLayout } from "docs-ui"
@@ -40,7 +38,6 @@ export default function RootLayout({
return (
<TightLayout
ProvidersComponent={Providers}
NavbarComponent={Navbar}
sidebarProps={{
expandItems: true,
}}

View File

@@ -1,38 +0,0 @@
"use client"
import { Navbar as UiNavbar, getNavbarItems } from "docs-ui"
import { useSidebar } from "docs-ui"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
import { config } from "../../config"
const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
const navbarItems = useMemo(
() =>
getNavbarItems({
basePath: config.baseUrl,
activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/user-guide",
version: "v2",
}),
[]
)
return (
<UiNavbar
logo={{
light: basePathUrl("/images/logo-icon.png"),
dark: basePathUrl("/images/logo-icon-dark.png"),
}}
items={navbarItems}
mobileMenuButton={{
setMobileSidebarOpen,
mobileSidebarOpen,
}}
isLoading={false}
/>
)
}
export default Navbar

View File

@@ -1,4 +1,4 @@
import { DocsConfig } from "types"
import { DocsConfig, SidebarItem } from "types"
import { getMobileSidebarItems } from "docs-ui"
import { generatedSidebar as sidebar } from "@/generated/sidebar.mjs"
@@ -9,8 +9,7 @@ export const config: DocsConfig = {
baseUrl,
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
sidebar: {
top: sidebar,
bottom: [],
default: sidebar as SidebarItem[],
mobile: getMobileSidebarItems({
baseUrl,
version: "v2",

View File

@@ -2,13 +2,18 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/",
"title": "Introduction",
"children": []
},
{
"type": "separator"
},
{
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Tips",
"hasTitleStyling": true,
"autogenerate_path": "/tips",
@@ -16,6 +21,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/tips/bulk-editor",
"title": "Bulk Editor",
"children": []
@@ -23,6 +29,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/tips/languages",
"title": "Admin Languages",
"children": []
@@ -30,6 +37,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/tips/lists",
"title": "Lists",
"children": []
@@ -39,6 +47,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/orders",
"title": "Orders",
"hasTitleStyling": true,
@@ -47,6 +56,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/manage",
"title": "Manage Details",
"children": []
@@ -54,6 +64,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/payments",
"title": "Manage Payments",
"children": []
@@ -61,6 +72,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/fulfillments",
"title": "Manage Fulfillments",
"children": []
@@ -68,6 +80,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/edit",
"title": "Edit an Orders Items",
"children": []
@@ -75,6 +88,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/drafts",
"title": "Manage Draft Orders",
"children": []
@@ -82,6 +96,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/returns",
"title": "Manage Returns",
"children": []
@@ -89,6 +104,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/exchange",
"title": "Manage Exchanges",
"children": []
@@ -96,6 +112,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/claims",
"title": "Manage Order's Claims",
"children": []
@@ -103,6 +120,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/orders/export",
"title": "Export Orders",
"children": []
@@ -112,6 +130,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/products",
"title": "Products",
"hasTitleStyling": true,
@@ -120,6 +139,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/manage",
"title": "Manage Products",
"children": []
@@ -127,6 +147,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/collections",
"title": "Manage Product Collections",
"children": []
@@ -134,6 +155,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/categories",
"title": "Manage Product Categories",
"children": []
@@ -141,12 +163,14 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/gift-cards",
"title": "Gift Cards",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/gift-cards/product-gift-card",
"title": "Manage a Product Gift Card",
"children": [],
@@ -155,6 +179,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/gift-cards/customer-gift-card",
"title": "Manage a Customer Gift Card",
"children": [],
@@ -165,6 +190,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/import",
"title": "Import Products",
"children": []
@@ -172,6 +198,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/products/export",
"title": "Export Products",
"children": []
@@ -181,6 +208,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/inventory",
"title": "Inventory",
"hasTitleStyling": true,
@@ -189,6 +217,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/inventory/inventory",
"title": "Manage Inventory",
"children": []
@@ -196,6 +225,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/inventory/reservations",
"title": "Manage Reservations",
"children": []
@@ -205,6 +235,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/customers",
"title": "Customers",
"hasTitleStyling": true,
@@ -213,6 +244,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/customers/manage",
"title": "Manage Customers",
"children": []
@@ -220,6 +252,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/customers/groups",
"title": "Manage Customer Groups",
"children": []
@@ -229,6 +262,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/discounts",
"title": "Discounts",
"hasTitleStyling": true,
@@ -237,6 +271,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/discounts/create",
"title": "Create a Discount",
"children": []
@@ -244,6 +279,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/discounts/manage",
"title": "Manage Discounts",
"children": []
@@ -253,6 +289,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/pricing",
"title": "Pricing",
"hasTitleStyling": true,
@@ -261,6 +298,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/pricing/create",
"title": "Create a Price List",
"children": []
@@ -268,6 +306,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/pricing/manage",
"title": "Manage Price Lists",
"children": []
@@ -277,6 +316,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "category",
"path": "/settings",
"title": "Settings",
"hasTitleStyling": true,
@@ -285,6 +325,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/profile",
"title": "Profile",
"children": []
@@ -292,6 +333,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/store",
"title": "Store",
"children": []
@@ -299,12 +341,14 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/users",
"title": "Users",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/users/invites",
"title": "Manage Invites",
"children": [],
@@ -315,12 +359,14 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/regions",
"title": "Regions",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/regions/manage",
"title": "Manage Regions",
"children": [],
@@ -329,6 +375,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/regions/countries",
"title": "Manage Countries",
"children": [],
@@ -337,6 +384,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/regions/providers",
"title": "Manage Providers",
"children": [],
@@ -345,6 +393,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/regions/shipping-options",
"title": "Manage Shipping Options",
"children": [],
@@ -355,6 +404,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/return-reasons",
"title": "Return Reasons",
"children": []
@@ -362,12 +412,14 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/taxes",
"title": "Taxes",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/taxes/manage",
"title": "Manage Taxes",
"children": [],
@@ -376,6 +428,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/taxes/tax-rates",
"title": "Manage Tax Rates",
"children": [],
@@ -386,6 +439,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/locations",
"title": "Locations",
"children": []
@@ -393,12 +447,14 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/sales-channels",
"title": "Sales Channels",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/sales-channels/manage",
"title": "Manage Sales Channels",
"children": [],
@@ -407,6 +463,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/sales-channels/products",
"title": "Manage Products",
"children": [],
@@ -417,18 +474,21 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/developer",
"title": "Developer",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/developer/api-key-management",
"title": "API Key Management",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/developer/api-key-management/sales-channels",
"title": "Manage Sales Channels",
"children": [],
@@ -440,6 +500,7 @@ export const generatedSidebar = [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/settings/developer/executions",
"title": "Executions",
"children": [],

View File

@@ -15,6 +15,7 @@ import {
import SidebarProvider from "./sidebar"
import SearchProvider from "./search"
import { config } from "../config"
import { MainNavProvider } from "./main-nav"
type ProvidersProps = {
children?: React.ReactNode
@@ -32,15 +33,17 @@ const Providers = ({ children }: ProvidersProps) => {
<ScrollControllerProvider scrollableSelector="#main">
<SidebarProvider>
<PaginationProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
<MainNavProvider>
<SearchProvider>
<HooksLoader
options={{
pageScrollManager: true,
}}
>
{children}
</HooksLoader>
</SearchProvider>
</MainNavProvider>
</PaginationProvider>
</SidebarProvider>
</ScrollControllerProvider>

View File

@@ -0,0 +1,46 @@
"use client"
import {
formatReportLink,
getNavDropdownItems,
MainNavProvider as UiMainNavProvider,
useIsBrowser,
} from "docs-ui"
import { useMemo } from "react"
import { config } from "../config"
import { basePathUrl } from "../utils/base-path-url"
type MainNavProviderProps = {
children?: React.ReactNode
}
export const MainNavProvider = ({ children }: MainNavProviderProps) => {
const isBrowser = useIsBrowser()
const navigationDropdownItems = useMemo(
() =>
getNavDropdownItems({
basePath: config.baseUrl,
activePath: basePathUrl(),
version: "v2",
}),
[]
)
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return (
<UiMainNavProvider
navItems={navigationDropdownItems}
reportIssueLink={reportLink}
>
{children}
</UiMainNavProvider>
)
}

View File

@@ -20,7 +20,6 @@ const SidebarProvider = ({ children }: SidebarProviderProps) => {
initialItems={config.sidebar}
staticSidebarItems={true}
disableActiveTransition={true}
noTitleStyling={true}
>
{children}
</UiSidebarProvider>

View File

@@ -5,51 +5,63 @@ import { sidebarAttachHrefCommonOptions } from "build-scripts"
/** @type {import('types').RawSidebarItemType[]} */
export const sidebar = sidebarAttachHrefCommonOptions([
{
type: "link",
path: "/",
title: "Introduction",
},
{
type: "separator",
},
{
type: "category",
title: "Tips",
hasTitleStyling: true,
autogenerate_path: "/tips",
},
{
type: "category",
path: "/orders",
title: "Orders",
hasTitleStyling: true,
autogenerate_path: "/orders",
},
{
type: "category",
path: "/products",
title: "Products",
hasTitleStyling: true,
autogenerate_path: "/products",
},
{
type: "category",
path: "/inventory",
title: "Inventory",
hasTitleStyling: true,
autogenerate_path: "/inventory",
},
{
type: "category",
path: "/customers",
title: "Customers",
hasTitleStyling: true,
autogenerate_path: "/customers",
},
{
type: "category",
path: "/discounts",
title: "Discounts",
hasTitleStyling: true,
autogenerate_path: "/discounts",
},
{
type: "category",
path: "/pricing",
title: "Pricing",
hasTitleStyling: true,
autogenerate_path: "/pricing",
},
{
type: "category",
path: "/settings",
title: "Settings",
hasTitleStyling: true,

View File

@@ -1,10 +1,10 @@
import type { RawSidebarItemType } from "types"
import type { RawSidebarItem, SidebarItem } from "types"
import { existsSync, mkdirSync, readdirSync, statSync } from "fs"
import path from "path"
import { getSidebarItemLink, sidebarAttachHrefCommonOptions } from "./index.js"
import getCoreFlowsRefSidebarChildren from "./utils/get-core-flows-ref-sidebar-children.js"
export type ItemsToAdd = RawSidebarItemType & {
export type ItemsToAdd = SidebarItem & {
sidebar_position?: number
}
@@ -37,19 +37,17 @@ async function getSidebarItems(
true
)
if (nested && newItems.length > 1) {
items.push(
...sidebarAttachHrefCommonOptions([
{
title:
fileBasename.charAt(0).toUpperCase() +
fileBasename.substring(1),
hasTitleStyling: true,
children: newItems,
},
])
)
items.push({
type: "sub-category",
title:
fileBasename.charAt(0).toUpperCase() + fileBasename.substring(1),
children: newItems,
loaded: true,
})
} else {
items.push(...sidebarAttachHrefCommonOptions(newItems))
items.push(
...(sidebarAttachHrefCommonOptions(newItems) as ItemsToAdd[])
)
}
continue
}
@@ -69,7 +67,11 @@ async function getSidebarItems(
mainPageIndex = items.length - 1
}
if (mainPageIndex !== -1 && items.length > 1) {
if (
mainPageIndex !== -1 &&
items.length > 1 &&
items[0].type !== "separator"
) {
// push all other items to be children of that page.
const mainPageChildren = [
...items.splice(0, mainPageIndex),
@@ -84,9 +86,19 @@ async function getSidebarItems(
return items
}
async function checkItem(
item: RawSidebarItemType
): Promise<RawSidebarItemType> {
async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
if (!item.type) {
throw new Error(
`ERROR: The following item doesn't have a type: ${JSON.stringify(
item,
undefined,
2
)}`
)
}
if (item.type === "separator") {
return item
}
if (item.autogenerate_path) {
item.children = (await getSidebarItems(item.autogenerate_path)).map(
(child) => {
@@ -109,7 +121,7 @@ async function checkItem(
return item
}
export async function generateSidebar(sidebar: RawSidebarItemType[]) {
export async function generateSidebar(sidebar: RawSidebarItem[]) {
const path = await import("path")
const { writeFileSync } = await import("fs")

View File

@@ -14,6 +14,19 @@ export default async function getCoreFlowsRefSidebarChildren(): Promise<
const sidebarItems: ItemsToAdd[] = []
sidebarItems.push(
{
type: "link",
title: "Overview",
path: "/medusa-workflows-reference",
loaded: true,
isPathHref: true,
},
{
type: "separator",
}
)
for (const directory of directories) {
if (
!directory.isDirectory() ||
@@ -66,21 +79,28 @@ export default async function getCoreFlowsRefSidebarChildren(): Promise<
if (workflowItems.length || stepItems.length) {
const item: ItemsToAdd = {
type: "category",
title: directory.name.replaceAll("_", " "),
children: [],
loaded: true,
initialOpen: false,
}
if (workflowItems.length) {
item.children!.push({
type: "sub-category",
title: "Workflows",
children: workflowItems,
loaded: true,
})
}
if (stepItems.length) {
item.children!.push({
type: "sub-category",
title: "Steps",
children: stepItems,
loaded: true,
})
}

View File

@@ -3,6 +3,7 @@ import { ItemsToAdd, sidebarAttachHrefCommonOptions } from "../index.js"
import { readFileSync } from "fs"
import findMetadataTitle from "./find-metadata-title.js"
import findPageHeading from "./find-page-heading.js"
import { InteractiveSidebarItem } from "types"
export async function getSidebarItemLink({
filePath,
@@ -24,6 +25,7 @@ export async function getSidebarItemLink({
const newItem = sidebarAttachHrefCommonOptions([
{
type: "link",
path:
frontmatter.slug ||
filePath.replace(basePath, "").replace(`/${fileBasename}`, ""),
@@ -33,7 +35,7 @@ export async function getSidebarItemLink({
findPageHeading(fileContent) ||
"",
},
])[0]
])[0] as InteractiveSidebarItem
return {
...newItem,

View File

@@ -1,16 +1,22 @@
import { RawSidebarItemType } from "types"
import { RawSidebarItem } from "types"
const commonOptions: Partial<RawSidebarItemType> = {
const commonOptions: Partial<RawSidebarItem> = {
loaded: true,
isPathHref: true,
}
export function sidebarAttachHrefCommonOptions(
sidebar: RawSidebarItemType[]
): RawSidebarItemType[] {
return sidebar.map((item) => ({
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || []),
}))
sidebar: RawSidebarItem[]
): RawSidebarItem[] {
return sidebar.map((item) => {
if (item.type === "separator") {
return item
}
return {
...commonOptions,
...item,
children: sidebarAttachHrefCommonOptions(item.children || []),
}
})
}

View File

@@ -78,6 +78,7 @@
"react-medium-image-zoom": "^5.1.10",
"react-tooltip": "^5.21.3",
"react-transition-group": "^4.4.5",
"react-uuid": "^2.0.0"
"react-uuid": "^2.0.0",
"slugify": "^1.6.6"
}
}

View File

@@ -1,15 +1,65 @@
import React from "react"
import { Card, Link } from "../.."
"use client"
import React, { useEffect, useState } from "react"
import { Button, useIsBrowser } from "../.."
import { ExclamationCircleSolid, XMark } from "@medusajs/icons"
import clsx from "clsx"
const LOCAL_STORAGE_KEY = "banner-v2"
export type Bannerv2Props = {
className?: string
}
export const Bannerv2 = ({ className }: Bannerv2Props) => {
const [show, setShow] = useState(false)
const isBrowser = useIsBrowser()
useEffect(() => {
if (!isBrowser) {
return
}
const localStorageValue = localStorage.getItem(LOCAL_STORAGE_KEY)
if (!localStorageValue) {
setShow(true)
}
}, [isBrowser])
const handleClose = () => {
setShow(false)
localStorage.setItem(LOCAL_STORAGE_KEY, "true")
}
export const Bannerv2 = () => {
return (
<Card>
This documentation is for Medusa v2, which isn&apos;t ready for
production.
<br />
<br />
For production-use, refer to{" "}
<Link href="https://docs.medusajs.com">this documentation</Link> instead.
</Card>
<div
className={clsx(
"bg-medusa-bg-base hidden gap-docs_0.5 z-20",
"justify-between items-start rounded-docs_DEFAULT",
"p-docs_0.75 shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark",
show && "lg:flex",
className
)}
>
<span className="p-[2.5px]">
<ExclamationCircleSolid className="text-medusa-tag-orange-icon" />
</span>
<div className="flex flex-col gap-docs_0.125 flex-1">
<span className="text-compact-small-plus text-medusa-fg-base">
Medusa v2 and Docs under development
</span>
<span className="text-compact-small text-medusa-fg-subtle">
We are actively working on building and improving. Some sections may
be incomplete or subject to change. Thank you for your patience.
</span>
</div>
<Button
variant="transparent-clear"
className="text-medusa-fg-muted"
onClick={handleClose}
>
<XMark />
</Button>
</div>
)
}

View File

@@ -1,5 +1,4 @@
import React from "react"
import { Bordered } from "@/components/Bordered"
import clsx from "clsx"
import { IconProps } from "@medusajs/icons/dist/types"
@@ -15,37 +14,35 @@ export type BorderedIconProps = {
export const BorderedIcon = ({
icon = "",
IconComponent = null,
wrapperClassName,
iconWrapperClassName,
iconClassName,
iconColorClassName = "",
}: BorderedIconProps) => {
return (
<Bordered wrapperClassName={wrapperClassName}>
<span
className={clsx(
"rounded-docs_xs p-docs_0.125 bg-medusa-bg-component inline-flex items-center justify-center",
iconWrapperClassName
)}
>
{!IconComponent && (
<img
src={icon || ""}
className={clsx(iconClassName, "bordered-icon")}
alt=""
/>
)}
{IconComponent && (
<IconComponent
className={clsx(
"text-medusa-fg-subtle",
iconClassName,
"bordered-icon",
iconColorClassName
)}
/>
)}
</span>
</Bordered>
<span
className={clsx(
"rounded-docs_sm p-docs_0.125 bg-medusa-bg-base inline-flex items-center justify-center",
"shadow-border-base dark:shadow-border-base-dark",
iconWrapperClassName
)}
>
{!IconComponent && (
<img
src={icon || ""}
className={clsx(iconClassName, "bordered-icon")}
alt=""
/>
)}
{IconComponent && (
<IconComponent
className={clsx(
"text-medusa-fg-subtle rounded-docs_sm",
iconClassName,
"bordered-icon",
iconColorClassName
)}
/>
)}
</span>
)
}

View File

@@ -17,8 +17,13 @@ export const Breadcrumbs = () => {
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
}
const parentPath =
item.parentItem?.type === "link" ? item.parentItem.path : undefined
const firstItemPath =
item.default[0].type === "link" ? item.default[0].path : undefined
tempBreadcrumbItems.set(
item.parentItem?.path || item.top[0].path || "/",
parentPath || firstItemPath || "/",
item.parentItem?.childSidebarTitle || item.parentItem?.title || ""
)

View File

@@ -2,7 +2,8 @@
import React, { useMemo } from "react"
import { Card, CardList, MDXComponents, useSidebar } from "../.."
import { SidebarItemType } from "types"
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
import slugify from "slugify"
type ChildDocsProps = {
onlyTopLevel?: boolean
@@ -30,16 +31,19 @@ export const ChildDocs = ({
: "all"
}, [showItems, hideItems])
const filterCondition = (item: SidebarItemType): boolean => {
const filterCondition = (item: SidebarItem): boolean => {
if (item.type === "separator") {
return false
}
switch (filterType) {
case "hide":
return (
(!item.path || !hideItems.includes(item.path)) &&
(item.type !== "link" || !hideItems.includes(item.path)) &&
!hideItems.includes(item.title)
)
case "show":
return (
(item.path !== undefined && showItems!.includes(item.path)) ||
(item.type === "link" && showItems!.includes(item.path)) ||
showItems!.includes(item.title)
)
case "all":
@@ -47,12 +51,16 @@ export const ChildDocs = ({
}
}
const filterItems = (items: SidebarItemType[]): SidebarItemType[] => {
const filterItems = (items: SidebarItem[]): SidebarItem[] => {
return items
.filter(filterCondition)
.map((item) => Object.assign({}, item))
.map((item) => {
if (item.children && filterType === "hide") {
if (
item.type !== "separator" &&
item.children &&
filterType === "hide"
) {
item.children = filterItems(item.children)
}
@@ -67,8 +75,7 @@ export const ChildDocs = ({
? Object.assign({}, currentItems)
: undefined
: {
top: [...(getActiveItem()?.children || [])],
bottom: [],
default: [...(getActiveItem()?.children || [])],
}
if (filterType === "all" || !targetItems) {
return targetItems
@@ -76,25 +83,34 @@ export const ChildDocs = ({
return {
...targetItems,
top: filterItems(targetItems.top),
bottom: filterItems(targetItems.bottom),
default: filterItems(targetItems.default),
}
}, [currentItems, type, getActiveItem, filterItems])
const filterNonInteractiveItems = (
items: SidebarItem[] | undefined
): InteractiveSidebarItem[] => {
return (
(items?.filter(
(item) => item.type !== "separator"
) as InteractiveSidebarItem[]) || []
)
}
const getChildrenForLevel = (
item: SidebarItemType,
item: InteractiveSidebarItem,
currentLevel = 1
): SidebarItemType[] | undefined => {
): InteractiveSidebarItem[] | undefined => {
if (currentLevel === childLevel) {
return item.children
return filterNonInteractiveItems(item.children)
}
if (!item.children) {
return
}
const childrenResult: SidebarItemType[] = []
const childrenResult: InteractiveSidebarItem[] = []
item.children.forEach((child) => {
filterNonInteractiveItems(item.children).forEach((child) => {
const childChildren = getChildrenForLevel(child, currentLevel + 1)
if (!childChildren) {
@@ -107,20 +123,34 @@ export const ChildDocs = ({
return childrenResult
}
const getTopLevelElms = (items?: SidebarItemType[]) => (
<CardList
items={
items?.map((childItem) => ({
title: childItem.title,
href: childItem.path,
showLinkIcon: false,
})) || []
}
/>
)
const getTopLevelElms = (items?: SidebarItem[]) => {
return (
<CardList
items={
filterNonInteractiveItems(items).map((childItem) => {
const href =
childItem.type === "link"
? childItem.path
: childItem.children?.length
? (
childItem.children.find(
(item) => item.type === "link"
) as SidebarItemLink
)?.path
: "#"
return {
title: childItem.title,
href,
showLinkIcon: false,
}
}) || []
}
/>
)
}
const getAllLevelsElms = (items?: SidebarItemType[]) =>
items?.map((item, key) => {
const getAllLevelsElms = (items?: SidebarItem[]) =>
filterNonInteractiveItems(items).map((item, key) => {
const itemChildren = getChildrenForLevel(item)
const HeadingComponent = itemChildren?.length
? MDXComponents["h2"]
@@ -130,12 +160,16 @@ export const ChildDocs = ({
<React.Fragment key={key}>
{HeadingComponent && (
<>
{!hideTitle && <HeadingComponent>{item.title}</HeadingComponent>}
{!hideTitle && (
<HeadingComponent id={slugify(item.title)}>
{item.title}
</HeadingComponent>
)}
<CardList
items={
itemChildren?.map((childItem) => ({
title: childItem.title,
href: childItem.path,
href: childItem.type === "link" ? childItem.path : "",
showLinkIcon: false,
})) || []
}
@@ -143,20 +177,19 @@ export const ChildDocs = ({
</>
)}
{!HeadingComponent && (
<Card title={item.title} href={item.path} showLinkIcon={false} />
<Card
title={item.title}
href={item.type === "link" ? item.path : ""}
showLinkIcon={false}
/>
)}
</React.Fragment>
)
})
const getElms = (items?: SidebarItemType[]) => {
const getElms = (items?: SidebarItem[]) => {
return onlyTopLevel ? getTopLevelElms(items) : getAllLevelsElms(items)
}
return (
<>
{getElms(filteredItems?.top)}
{getElms(filteredItems?.bottom)}
</>
)
return <>{getElms(filteredItems?.default)}</>
}

View File

@@ -1,39 +1,23 @@
"use client"
import React, { useCallback, useEffect, useRef, useState } from "react"
import React, { useEffect, useRef, useState } from "react"
import { Button } from "@/components"
import clsx from "clsx"
import { CSSTransition } from "react-transition-group"
import { HelpButtonActions } from "./Actions"
import { useIsBrowser } from "../.."
import { useClickOutside } from "../.."
export const HelpButton = () => {
const [showText, setShowText] = useState(false)
const [showHelp, setShowHelp] = useState(false)
const ref = useRef<HTMLDivElement>(null)
const isBrowser = useIsBrowser()
const onClickOutside = useCallback(
(e: MouseEvent) => {
if (!ref.current?.contains(e.target as Node)) {
setShowHelp(false)
setShowText(false)
}
useClickOutside({
elmRef: ref,
onClickOutside: () => {
setShowHelp(false)
setShowText(false)
},
[ref.current]
)
useEffect(() => {
if (!isBrowser) {
return
}
window.document.addEventListener("click", onClickOutside)
return () => {
window.document.removeEventListener("click", onClickOutside)
}
}, [isBrowser])
})
useEffect(() => {
if (showHelp) {

View File

@@ -0,0 +1,23 @@
import React from "react"
import { IconProps } from "@medusajs/icons/dist/types"
export const HouseIcon = (props: IconProps) => {
return (
<svg
width="15"
height="16"
viewBox="0 0 15 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M12.7044 5.5112L8.03778 1.96454C7.71956 1.72276 7.27956 1.72276 6.96222 1.96454L2.29556 5.5112C2.07422 5.6792 1.94444 5.94143 1.94444 6.21965V12.6676C1.94444 13.6499 2.74 14.4454 3.72222 14.4454H5.94444V10.8899C5.94444 10.3992 6.34267 10.001 6.83333 10.001H8.16667C8.65733 10.001 9.05556 10.3992 9.05556 10.8899V14.4454H11.2778C12.26 14.4454 13.0556 13.6499 13.0556 12.6676V6.21876C13.0556 5.94054 12.9258 5.68009 12.7044 5.5112Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownAdminIcon = (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}
>
<rect width="20" height="20" className="fill-medusa-tag-green-icon" />
<g clipPath="url(#clip0_9988_95571)">
<path
d="M13.25 4.5H6.75C5.233 4.5 4 5.733 4 7.25V12.75C4 14.267 5.233 15.5 6.75 15.5H13.25C14.767 15.5 16 14.267 16 12.75V7.25C16 5.733 14.767 4.5 13.25 4.5ZM6.5 8C5.948 8 5.5 7.552 5.5 7C5.5 6.448 5.948 6 6.5 6C7.052 6 7.5 6.448 7.5 7C7.5 7.552 7.052 8 6.5 8ZM9.5 8C8.948 8 8.5 7.552 8.5 7C8.5 6.448 8.948 6 9.5 6C10.052 6 10.5 6.448 10.5 7C10.5 7.552 10.052 8 9.5 8Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_9988_95571">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,41 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownDocIcon = (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}
>
<rect
width="20"
height="20"
className="fill-medusa-fg-base dark:fill-medusa-bg-base"
/>
<g clipPath="url(#clip0_9988_95547)">
<path
d="M14.25 16H7.25C6.009 16 5 14.991 5 13.75C5 13.336 5.336 13 5.75 13C6.164 13 6.5 13.336 6.5 13.75C6.5 14.164 6.836 14.5 7.25 14.5H14.25C14.664 14.5 15 14.836 15 15.25C15 15.664 14.664 16 14.25 16Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M12.75 4H7.25C6.009 4 5 5.009 5 6.25V13.75C5 14.164 5.336 14.5 5.75 14.5C6.164 14.5 6.5 14.164 6.5 13.75C6.5 13.336 6.836 13 7.25 13H14.25C14.664 13 15 12.664 15 12.25V6.25C15 5.009 13.991 4 12.75 4ZM11.25 9H8.75C8.336 9 8 8.664 8 8.25C8 7.836 8.336 7.5 8.75 7.5H11.25C11.664 7.5 12 7.836 12 8.25C12 8.664 11.664 9 11.25 9Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_9988_95547">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,37 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownDocV1Icon = (props: IconProps) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect width="20" height="20" className="fill-medusa-fg-subtle" />
<g clipPath="url(#clip0_10088_101562)">
<path
d="M8 4.02405C6.687 4.14505 5.646 5.18605 5.525 6.50005H4.75C4.336 6.50005 4 6.83605 4 7.25005C4 7.66405 4.336 8.00005 4.75 8.00005H5.5V9.25005H4.75C4.336 9.25005 4 9.58605 4 10C4 10.414 4.336 10.75 4.75 10.75H5.5V12H4.75C4.336 12 4 12.336 4 12.75C4 13.164 4.336 13.5 4.75 13.5H5.525C5.646 14.814 6.687 15.855 8 15.976V4.02405Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M12.25 4H9.5V16H12.25C13.767 16 15 14.767 15 13.25V6.75C15 5.233 13.767 4 12.25 4Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_10088_101562">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,37 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownResourcesIcon = (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}
>
<rect width="20" height="20" className="fill-medusa-tag-orange-icon" />
<g clipPath="url(#clip0_9988_95555)">
<path
d="M15.25 12C14.836 12 14.5 11.664 14.5 11.25V8.75C14.5 8.061 13.939 7.5 13.25 7.5H10.614C10.386 7.5 10.172 7.397 10.029 7.219L9.425 6.467C9.187 6.17 8.831 6 8.45 6H6.749C6.06 6 5.499 6.561 5.499 7.25V11.25C5.499 11.664 5.163 12 4.749 12C4.335 12 3.999 11.664 3.999 11.25V7.25C4 5.733 5.233 4.5 6.75 4.5H8.451C9.289 4.5 10.07 4.875 10.596 5.528L10.974 6H13.25C14.767 6 16 7.233 16 8.75V11.25C16 11.664 15.664 12 15.25 12Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M13.25 8.5H6.75C5.23122 8.5 4 9.73122 4 11.25V12.75C4 14.2688 5.23122 15.5 6.75 15.5H13.25C14.7688 15.5 16 14.2688 16 12.75V11.25C16 9.73122 14.7688 8.5 13.25 8.5Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_9988_95555">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,37 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownStoreIcon = (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}
>
<rect width="20" height="20" className="fill-medusa-tag-purple-icon" />
<g clipPath="url(#clip0_9988_95563)">
<path
d="M15.25 8.548C15.122 8.548 14.993 8.515 14.874 8.446L10 5.617L5.126 8.446C4.77 8.654 4.309 8.532 4.101 8.174C3.893 7.816 4.015 7.357 4.373 7.149L9.624 4.102C9.856 3.966 10.145 3.966 10.377 4.102L15.627 7.15C15.985 7.358 16.107 7.817 15.899 8.175C15.76 8.415 15.508 8.548 15.25 8.548Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M10 7.35195L5 10.254V13.75C5 14.715 5.785 15.5 6.75 15.5H9.25V13.25C9.25 12.836 9.586 12.5 10 12.5C10.414 12.5 10.75 12.836 10.75 13.25V15.5H13.25C14.215 15.5 15 14.715 15 13.75V10.254L10 7.35195Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_9988_95563">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,37 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownUiIcon = (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}
>
<rect width="20" height="20" className="fill-medusa-tag-blue-icon" />
<g clipPath="url(#clip0_9988_95578)">
<path
d="M14.18 10.472L9.28899 8.685C8.82599 8.518 8.32399 8.627 7.97699 8.975C7.62799 9.323 7.51699 9.826 7.68599 10.288L9.47199 15.178C9.65599 15.679 10.114 16 10.645 16C10.654 16 10.664 16 10.672 16C11.215 15.989 11.672 15.648 11.836 15.132L12.394 13.394L14.131 12.838C14.648 12.673 14.988 12.216 15 11.673C15.011 11.131 14.689 10.658 14.18 10.472Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M6.13499 11.894C6.05799 11.894 5.97999 11.882 5.90299 11.857C4.76499 11.487 4.00099 10.439 4.00099 9.25V7.75C3.99999 6.234 5.23299 5 6.74999 5H13.25C14.767 5 16 6.234 16 7.75V9.052C16 9.466 15.664 9.802 15.25 9.802C14.836 9.802 14.5 9.466 14.5 9.052V7.75C14.5 7.061 13.939 6.5 13.25 6.5H6.74999C6.06099 6.5 5.49999 7.061 5.49999 7.75V9.25C5.49999 9.788 5.84899 10.262 6.36699 10.43C6.76099 10.558 6.97599 10.981 6.84699 11.375C6.74399 11.692 6.45099 11.893 6.13399 11.893L6.13499 11.894Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_9988_95578">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,37 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const NavigationDropdownUserIcon = (props: IconProps) => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<rect width="20" height="20" className="fill-medusa-tag-red-icon" />
<g clipPath="url(#clip0_10088_101526)">
<path
d="M10 8.99097C11.3807 8.99097 12.5 7.87168 12.5 6.49097C12.5 5.11025 11.3807 3.99097 10 3.99097C8.61929 3.99097 7.5 5.11025 7.5 6.49097C7.5 7.87168 8.61929 8.99097 10 8.99097Z"
className="fill-medusa-fg-on-color"
/>
<path
d="M14.533 12.639C13.601 11.011 11.864 10 10 10C8.136 10 6.398 11.011 5.467 12.639C5.218 13.073 5.162 13.593 5.313 14.067C5.463 14.539 5.809 14.93 6.26 15.139C7.501 15.713 8.75 16 10 16C11.25 16 12.499 15.713 13.74 15.139C14.191 14.93 14.536 14.539 14.687 14.067C14.838 13.593 14.782 13.073 14.533 12.64V12.639Z"
className="fill-medusa-fg-on-color"
/>
</g>
<defs>
<clipPath id="clip0_10088_101526">
<rect
width="12"
height="12"
className="fill-medusa-fg-on-color"
transform="translate(4 4)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -0,0 +1,42 @@
import { IconProps } from "@medusajs/icons/dist/types"
import React from "react"
export const SidebarLeftIcon = (props: IconProps) => {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g clipPath="url(#clip0_10002_37978)">
<path
d="M12.6668 2.25H3.33344C2.3516 2.25 1.55566 3.0738 1.55566 4.09V11.91C1.55566 12.9262 2.3516 13.75 3.33344 13.75H12.6668C13.6486 13.75 14.4446 12.9262 14.4446 11.91V4.09C14.4446 3.0738 13.6486 2.25 12.6668 2.25Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M4.3999 5V11"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0_10002_37978">
<rect
width="15"
height="15"
fill="none"
transform="translate(0.5 0.5)"
/>
</clipPath>
</defs>
</svg>
)
}

View File

@@ -9,9 +9,9 @@ export const Kbd = ({ children, className, ...props }: KbdProps) => {
className={clsx(
"rounded-docs_xs border-solid border border-medusa-border-base",
"inline-flex items-center justify-center",
"py-0 px-[6px]",
"py-0 px-docs_0.25",
"bg-medusa-bg-field",
"text-medusa-tag-neutral-text",
"text-medusa-fg-subtle",
"text-compact-x-small-plus font-base shadow-none",
className
)}

View File

@@ -0,0 +1,84 @@
"use client"
import React, { useMemo } from "react"
import { CurrentItemsState, useSidebar } from "../../.."
import clsx from "clsx"
import Link from "next/link"
import { SidebarItemLink } from "types"
export const MainNavBreadcrumbs = () => {
const { currentItems, getActiveItem } = useSidebar()
const getLinkPath = (item?: SidebarItemLink): string | undefined => {
if (!item) {
return
}
return item.isPathHref ? item.path : `#${item.path}`
}
const getBreadcrumbsOfItem = (
item: CurrentItemsState
): Map<string, string> => {
let tempBreadcrumbItems: Map<string, string> = new Map()
if (item.previousSidebar) {
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
}
const parentPath =
item.parentItem?.type === "link"
? getLinkPath(item.parentItem)
: undefined
const firstItemPath =
item.default[0].type === "link" ? getLinkPath(item.default[0]) : undefined
tempBreadcrumbItems.set(
parentPath || firstItemPath || "/",
item.parentItem?.childSidebarTitle || item.parentItem?.title || ""
)
return tempBreadcrumbItems
}
const breadcrumbItems = useMemo(() => {
const tempBreadcrumbItems: Map<string, string> = new Map()
if (currentItems) {
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
tempBreadcrumbItems.set(key, value)
)
}
const activeItem = getActiveItem()
if (activeItem) {
tempBreadcrumbItems.set(
getLinkPath(activeItem) || "/",
activeItem?.title || ""
)
}
return tempBreadcrumbItems
}, [currentItems, getActiveItem])
return (
<div
className={clsx(
"flex items-center",
"text-medusa-fg-muted text-compact-small"
)}
>
{Array.from(breadcrumbItems).map(([link, title]) => (
<React.Fragment key={link}>
<span>/</span>
<Link
href={link}
className={clsx(
"hover:text-medusa-fg-base transition-colors",
"px-docs_0.5 py-docs_0.25"
)}
>
{title}
</Link>
</React.Fragment>
))}
</div>
)
}

View File

@@ -0,0 +1,22 @@
"use client"
import React from "react"
import { useColorMode } from "../../../providers"
import { Button } from "../../.."
import { Moon, Sun } from "@medusajs/icons"
import clsx from "clsx"
export const MainNavColorMode = () => {
const { colorMode, toggleColorMode } = useColorMode()
return (
<Button
variant="transparent-clear"
className={clsx("!p-[6.5px] text-medusa-fg-muted")}
onClick={toggleColorMode}
>
{colorMode === "light" && <Sun />}
{colorMode === "dark" && <Moon />}
</Button>
)
}

View File

@@ -0,0 +1,13 @@
import clsx from "clsx"
import React from "react"
export const MainNavDivider = () => {
return (
<span
className={clsx(
"h-docs_0.75 w-px block bg-medusa-border-base",
"mx-docs_0.5"
)}
></span>
)
}

View File

@@ -0,0 +1,21 @@
import React from "react"
import { BorderedIcon } from "@/components"
import { IconProps } from "@medusajs/icons/dist/types"
export type MainNavigationDropdownIconProps = {
icon: React.FC<IconProps>
inDropdown?: boolean
}
export const MainNavigationDropdownIcon = ({
icon,
inDropdown = false,
}: MainNavigationDropdownIconProps) => {
return (
<BorderedIcon
IconComponent={icon}
iconClassName={inDropdown ? "w-docs_1 h-docs_1" : ""}
iconWrapperClassName="rounded-docs_xs"
/>
)
}

View File

@@ -0,0 +1,50 @@
"use client"
import React from "react"
import { NavigationDropdownItem } from "types"
import Link from "next/link"
import clsx from "clsx"
import { EllipseMiniSolid } from "@medusajs/icons"
import { MainNavigationDropdownIcon } from "../../Icon"
import { SidebarSeparator } from "../../../../Sidebar/Separator"
export type MainNavigationDropdownMenuItemProps = {
item: NavigationDropdownItem
onSelect: () => void
}
export const MainNavigationDropdownMenuItem = ({
item,
onSelect,
}: MainNavigationDropdownMenuItemProps) => {
switch (item.type) {
case "divider":
return <SidebarSeparator className="my-docs_0.25" />
case "link":
return (
<Link
href={item.path}
className={clsx(
"hover:bg-medusa-bg-component-hover",
"block rounded-docs_xs"
)}
prefetch={false}
onClick={onSelect}
>
<li
className={clsx(
"px-docs_0.5 py-docs_0.25",
"rounded-docs_xs text-medusa-fg-base",
"flex gap-docs_0.5 justify-start items-center"
)}
>
<span className={clsx(!item.isActive && "invisible")}>
<EllipseMiniSolid />
</span>
<MainNavigationDropdownIcon icon={item.icon} inDropdown={true} />
<span className="whitespace-nowrap">{item.title}</span>
</li>
</Link>
)
}
}

View File

@@ -0,0 +1,36 @@
"use client"
import clsx from "clsx"
import React from "react"
import { NavigationDropdownItem } from "types"
import { MainNavigationDropdownMenuItem } from "./Item"
export type MainNavigationDropdownMenuProps = {
items: NavigationDropdownItem[]
open: boolean
onSelect: () => void
}
export const MainNavigationDropdownMenu = ({
items,
open,
onSelect,
}: MainNavigationDropdownMenuProps) => {
return (
<ul
className={clsx(
"absolute top-[calc(100%+4px)] p-docs_0.25 z-50 lg:-left-docs_0.25",
"bg-medusa-bg-component rounded shadow-elevation-flyout",
!open && "hidden"
)}
>
{items.map((item, index) => (
<MainNavigationDropdownMenuItem
item={item}
key={index}
onSelect={onSelect}
/>
))}
</ul>
)
}

View File

@@ -0,0 +1,40 @@
"use client"
import React from "react"
import { NavigationDropdownItem } from "types"
import { TrianglesMini } from "@medusajs/icons"
import clsx from "clsx"
import { MainNavigationDropdownIcon } from "../Icon"
export type MainNavigationDropdownSelectedProps = {
item: NavigationDropdownItem
onClick: () => void
}
export const MainNavigationDropdownSelected = ({
item,
onClick,
}: MainNavigationDropdownSelectedProps) => {
if (item.type === "divider") {
return <></>
}
return (
<div
className={clsx(
"flex justify-between items-center",
"cursor-pointer rounded-docs_sm hover:bg-medusa-bg-hover"
)}
tabIndex={-1}
onClick={onClick}
>
<MainNavigationDropdownIcon icon={item.icon} />
<div className="flex gap-[6px] py-docs_0.25 px-docs_0.5 items-center">
<span className="text-medusa-fg-base whitespace-nowrap flex-1">
{item.title}
</span>
<TrianglesMini className="text-medusa-fg-muted" />
</div>
</div>
)
}

View File

@@ -0,0 +1,46 @@
"use client"
import clsx from "clsx"
import React, { useMemo, useRef, useState } from "react"
import { MainNavigationDropdownSelected } from "./Selected"
import { MainNavigationDropdownMenu } from "./Menu"
import { useClickOutside, useMainNav } from "../../.."
export const MainNavigationDropdown = () => {
const { navItems: items } = useMainNav()
const navigationRef = useRef<HTMLDivElement>(null)
const [menuOpen, setMenuOpen] = useState(false)
useClickOutside({
elmRef: navigationRef,
onClickOutside: () => {
setMenuOpen(false)
},
})
const selectedItem = useMemo(() => {
const activeItem = items.find(
(item) => item.type === "link" && item.isActive
)
if (!activeItem) {
return items.length ? items[0] : undefined
}
return activeItem
}, [items])
return (
<div className={clsx("relative z-50")} ref={navigationRef}>
{selectedItem && (
<MainNavigationDropdownSelected
item={selectedItem}
onClick={() => setMenuOpen((prev) => !prev)}
/>
)}
<MainNavigationDropdownMenu
items={items}
open={menuOpen}
onSelect={() => setMenuOpen(false)}
/>
</div>
)
}

View File

@@ -0,0 +1,24 @@
"use client"
import React from "react"
import { Button, useSidebar } from "../../.."
import clsx from "clsx"
import { SidebarLeft } from "@medusajs/icons"
export const MainNavSidebarOpener = () => {
const { desktopSidebarOpen, setDesktopSidebarOpen } = useSidebar()
if (desktopSidebarOpen) {
return <></>
}
return (
<Button
variant="transparent-clear"
className={clsx("!p-[6.5px] text-medusa-fg-muted", "mr-docs_0.5")}
onClick={() => setDesktopSidebarOpen(true)}
>
<SidebarLeft />
</Button>
)
}

View File

@@ -0,0 +1,38 @@
"use client"
import clsx from "clsx"
import React from "react"
import { MainNavigationDropdown } from "./NavigationDropdown"
import { MainNavBreadcrumbs } from "./Breadcrumb"
import { SearchModalOpener, useMainNav } from "../.."
import { MainNavColorMode } from "./ColorMode"
import Link from "next/link"
import { MainNavDivider } from "./Divider"
import { MainNavSidebarOpener } from "./SidebarOpener"
export const MainNav = () => {
const { reportIssueLink } = useMainNav()
return (
<div
className={clsx(
"hidden sm:flex justify-between items-center",
"px-docs_1 py-docs_0.75 w-full z-20",
"sticky top-0 bg-medusa-bg-base"
)}
>
<div className="flex items-center gap-docs_0.25">
<MainNavSidebarOpener />
<MainNavigationDropdown />
<MainNavBreadcrumbs />
</div>
<div className="flex items-center gap-docs_0.25">
<Link href={reportIssueLink} className="text-medusa-fg-muted">
Report Issue
</Link>
<MainNavDivider />
<MainNavColorMode />
<SearchModalOpener />
</div>
</div>
)
}

View File

@@ -0,0 +1,32 @@
"use client"
import clsx from "clsx"
import React from "react"
import { MenuItemAction } from "types"
export type MenuActionProps = {
item: MenuItemAction
}
export const MenuAction = ({ item }: MenuActionProps) => {
return (
<span
className={clsx(
"flex py-docs_0.25 px-docs_0.5",
"gap-docs_0.5 rounded-docs_xs",
"hover:bg-medusa-bg-component-hover",
"text-medusa-fg-base cursor-pointer"
)}
tabIndex={-1}
onClick={item.action}
>
<span className="text-medusa-fg-subtle">{item.icon}</span>
<span className="text-compact-small flex-1">{item.title}</span>
{item.shortcut && (
<span className="text-medusa-fg-subtle text-compact-small">
{item.shortcut}
</span>
)}
</span>
)
}

View File

@@ -0,0 +1,7 @@
"use client"
import React from "react"
export const MenuDivider = () => {
return <hr className="bg-medusa-border-menu-top mt-[3px] mb-[3px]" />
}

View File

@@ -0,0 +1,27 @@
"use client"
import clsx from "clsx"
import Link from "next/link"
import React from "react"
import { MenuItemLink } from "types"
export type MenuItemProps = {
item: MenuItemLink
}
export const MenuItem = ({ item }: MenuItemProps) => {
return (
<Link
className={clsx(
"flex py-docs_0.25 px-docs_0.5",
"gap-docs_0.5 rounded-docs_xs",
"hover:bg-medusa-bg-component-hover",
"text-medusa-fg-base"
)}
href={item.link}
>
<span className="text-medusa-fg-subtle">{item.icon}</span>
<span className="text-compact-small">{item.title}</span>
</Link>
)
}

View File

@@ -0,0 +1,31 @@
import clsx from "clsx"
import React from "react"
import { MenuItem as MenuItemType } from "types"
import { MenuItem } from "./Item"
import { MenuDivider } from "./Divider"
import { MenuAction } from "./Action"
export type MenuProps = {
items: MenuItemType[]
className?: string
}
export const Menu = ({ items, className }: MenuProps) => {
return (
<div
className={clsx(
"bg-medusa-bg-component p-docs_0.25 rounded-docs_DEFAULT",
"shadow-elevation-flyout dark:shadow-elevation-flyout-dark",
className
)}
>
{items.map((item, index) => (
<React.Fragment key={index}>
{item.type === "link" && <MenuItem item={item} />}
{item.type === "action" && <MenuAction item={item} />}
{item.type === "divider" && <MenuDivider />}
</React.Fragment>
))}
</div>
)
}

View File

@@ -0,0 +1,32 @@
"use client"
import clsx from "clsx"
import React from "react"
import { SidebarLeftIcon } from "../Icons/SidebarLeft"
import { Button, SearchModalOpener, useSidebar } from "../.."
import { MainNavigationDropdown } from "../MainNav/NavigationDropdown"
export const MobileNavigation = () => {
const { setMobileSidebarOpen } = useSidebar()
return (
<div
className={clsx(
"sm:hidden bg-medusa-bg-base",
"sticky top-0 w-full z-50 h-min",
"px-docs_0.75 py-docs_0.5",
"flex justify-between items-center",
"border-b border-medusa-border-base"
)}
>
<Button
variant="transparent-clear"
onClick={() => setMobileSidebarOpen(true)}
>
<SidebarLeftIcon />
</Button>
<MainNavigationDropdown />
<SearchModalOpener />
</div>
)
}

View File

@@ -88,7 +88,7 @@ export const Modal = ({
{...props}
className={clsx(
"fixed top-0 left-0 flex h-screen w-screen items-center justify-center",
"bg-medusa-bg-overlay",
"bg-medusa-bg-overlay z-50",
"hidden open:flex border-0 p-0",
className
)}

View File

@@ -1,23 +0,0 @@
"use client"
import React from "react"
import { NavbarIconButton, NavbarIconButtonProps } from "../IconButton"
import { useColorMode } from "@/providers"
import { Moon, Sun } from "@medusajs/icons"
export type NavbarColorModeToggleProps = {
buttonProps?: NavbarIconButtonProps
}
export const NavbarColorModeToggle = ({
buttonProps,
}: NavbarColorModeToggleProps) => {
const { colorMode, toggleColorMode } = useColorMode()
return (
<NavbarIconButton {...buttonProps} onClick={() => toggleColorMode()}>
{colorMode === "light" && <Sun className="text-medusa-fg-muted" />}
{colorMode === "dark" && <Moon className="text-medusa-fg-muted" />}
</NavbarIconButton>
)
}

View File

@@ -1,24 +0,0 @@
import React from "react"
import clsx from "clsx"
import { Button, ButtonProps } from "@/components"
export type NavbarIconButtonProps = ButtonProps
export const NavbarIconButton = ({
children,
className,
...props
}: NavbarIconButtonProps) => {
return (
<Button
className={clsx(
"[&>svg]:h-[22px] [&>svg]:w-[22px] btn-secondary-icon",
className
)}
variant="secondary"
{...props}
>
{children}
</Button>
)
}

View File

@@ -1,40 +0,0 @@
"use client"
import React from "react"
import clsx from "clsx"
import { Badge, BadgeProps, Link, LinkProps } from "@/components"
export type NavbarLinkProps = {
href: string
label: string
className?: string
activeValuePattern?: RegExp
isActive?: boolean
badge?: BadgeProps
} & LinkProps
export const NavbarLink = ({
href,
label,
className,
isActive,
badge,
}: NavbarLinkProps) => {
return (
<Link
href={href}
className={clsx(
isActive && "!text-medusa-fg-base",
!isActive && "!text-medusa-fg-subtle",
"text-compact-small-plus inline-block",
"hover:!text-medusa-fg-base",
className
)}
>
{label}
{badge && (
<Badge {...badge} className={clsx(badge.className, "ml-docs_0.5")} />
)}
</Link>
)
}

View File

@@ -1,35 +0,0 @@
"use client"
import React from "react"
import { useColorMode } from "@/providers"
import Link from "next/link"
import clsx from "clsx"
import Image from "next/image"
export type NavbarLogoProps = {
light: string
dark?: string
className?: string
imageClassName?: string
}
export const NavbarLogo = ({
light,
dark,
className,
imageClassName,
}: NavbarLogoProps) => {
const { colorMode } = useColorMode()
return (
<Link href={`/`} className={clsx("flex-1", className)}>
<Image
src={colorMode === "light" ? light : dark || light}
alt="Medusa Logo"
height={20}
width={20}
className={clsx("align-middle", imageClassName)}
/>
</Link>
)
}

Some files were not shown because too many files have changed in this diff Show More