docs: preparations for preview (#7267)

* configured base paths + added development banner

* fix typelist site url

* added navbar and sidebar badges

* configure algolia filters

* remove AI assistant

* remove unused imports

* change navbar text and badge

* lint fixes

* fix build error

* add to api reference rewrites

* fix build error

* fix build errors in user-guide

* fix feedback component

* add parent title to pagination

* added breadcrumbs component

* remove user-guide links

* resolve todos

* fix details about authentication

* change documentation title

* lint content
This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-05-13 11:32:52 +03:00
1 parent 3e3b8a483f
commit 728c5ee53c
62 files changed
+855 -506

No files matched your search

+2 -1
View File
@@ -15,4 +15,5 @@ ANALYZE_BUNDLE=
NEXT_PUBLIC_AI_ASSISTANT_URL= NEXT_PUBLIC_AI_ASSISTANT_URL=
NEXT_PUBLIC_AI_WEBSITE_ID= NEXT_PUBLIC_AI_WEBSITE_ID=
NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY=
NEXT_PUBLIC_VERSIONING= NEXT_PUBLIC_VERSIONING=
NEXT_PUBLIC_SHOW_V2=
@@ -24,6 +24,7 @@ const Navbar = () => {
getNavbarItems({ getNavbarItems({
basePath: config.baseUrl, basePath: config.baseUrl,
activePath: pathname, activePath: pathname,
version: process.env.NEXT_PUBLIC_SHOW_V2 ? "v1" : "legacy",
}), }),
[pathname] [pathname]
) )
+2 -2
View File
@@ -1,5 +1,5 @@
import { DocsConfig } from "types" import { DocsConfig } from "types"
import { mobileSidebarItems } from "docs-ui" import { mobileSidebarItemsV1, legacyMobileSidebarItems } from "docs-ui"
export const config: DocsConfig = { export const config: DocsConfig = {
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000", baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
@@ -13,6 +13,6 @@ export const config: DocsConfig = {
}, },
], ],
bottom: [], bottom: [],
mobile: mobileSidebarItems, mobile: process.env.NEXT_PUBLIC_SHOW_V2 ? mobileSidebarItemsV1 : legacyMobileSidebarItems,
}, },
} }
+37 -23
View File
@@ -4,30 +4,44 @@ import bundleAnalyzer from "@next/bundle-analyzer"
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
async rewrites() { async rewrites() {
const fallbacks = [
{
source: "/ui",
destination: `${process.env.NEXT_PUBLIC_UI_URL}/ui`,
},
{
source: "/ui/:path*",
destination: `${process.env.NEXT_PUBLIC_UI_URL}/ui/:path*`,
},
{
source: "/:path*",
destination: `${process.env.NEXT_PUBLIC_DOCS_URL}/:path*`,
},
]
if (process.env.NEXT_PUBLIC_SHOW_V2) {
fallbacks.push(
{
source: "/v2/resources",
destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/v2/resources`,
},
{
source: "/v2/resources/:path*",
destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/v2/resources/:path*`,
},
{
source: "/v2",
destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/v2`,
},
{
source: "/v2/:path*",
destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/v2/:path*`,
}
)
}
return { return {
fallback: [ fallback: fallbacks,
{
source: "/ui",
destination: `${process.env.NEXT_PUBLIC_UI_URL}/ui`,
},
{
source: "/ui/:path*",
destination: `${process.env.NEXT_PUBLIC_UI_URL}/ui/:path*`,
},
// TODO uncomment for v2
// {
// source: "/v2",
// destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/resources`,
// },
// {
// source: "/v2/:path*",
// destination: `${process.env.NEXT_PUBLIC_DOCS_V2_URL}/resources/:path*`,
// },
{
source: "/:path*",
destination: `${process.env.NEXT_PUBLIC_DOCS_URL}/:path*`,
},
],
} }
}, },
webpack: (config) => { webpack: (config) => {
+2 -2
View File
@@ -5,7 +5,7 @@ import {
SearchProvider as UiSearchProvider, SearchProvider as UiSearchProvider,
AiAssistantCommandIcon, AiAssistantCommandIcon,
AiAssistantProvider, AiAssistantProvider,
searchFilters, searchFiltersV1,
} from "docs-ui" } from "docs-ui"
import { config } from "../config" import { config } from "../config"
@@ -53,7 +53,7 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
checkInternalPattern: new RegExp( checkInternalPattern: new RegExp(
`^${config.baseUrl}/api/(admin|store)` `^${config.baseUrl}/api/(admin|store)`
), ),
filterOptions: searchFilters, filterOptions: searchFiltersV1,
}} }}
commands={[ commands={[
{ {
@@ -1,7 +1,6 @@
"use client" "use client"
import { import {
SidebarProvider as UiSidebarProvider, SidebarProvider as UiSidebarProvider,
mobileSidebarItems,
usePageLoading, usePageLoading,
useScrollController, useScrollController,
} from "docs-ui" } from "docs-ui"
+2 -1
View File
@@ -14,4 +14,5 @@ ANALYZE_BUNDLE=
NEXT_PUBLIC_AI_ASSISTANT_URL= NEXT_PUBLIC_AI_ASSISTANT_URL=
NEXT_PUBLIC_AI_WEBSITE_ID= NEXT_PUBLIC_AI_WEBSITE_ID=
NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY=
CLOUDINARY_CLOUD_NAME= CLOUDINARY_CLOUD_NAME=
NEXT_PUBLIC_BASE_PATH=
@@ -118,36 +118,43 @@ In the route handler, you resolve the `UserService`, and then use it to retrieve
## Protect Custom API Routes ## Protect Custom API Routes
To protect custom API Routes that don’t start with `/store/me` or `/admin`, apply one of the following middlewares exported by the `@medusajs/medusa` package on your routes: To protect custom API Routes that don’t start with `/store/me` or `/admin`, use the `authenticate` middleware imported from `@medusajs/medusa`.
- `authenticate`: only authenticated admin users can access the API Route.
- `authenticateCustomer`: customer authentication isn’t required, but if a customer is logged in, it attaches their ID to the `MedusaRequest` object's `user.customer_id`.
- `requireCustomerAuthentication`: only authenticated customers can access the API Route.
For example: For example:
export const highlights = [ export const highlights = [
["11", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`"], ["11", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`"],
["15", "requireCustomerAuthentication", "Only authenticated customers can access routes starting with `/custom/customers`"] ["17", "authenticate", "Only authenticated customers can access routes starting with `/custom/customers`"]
] ]
```ts title="src/api/middlewares.ts" highlights={highlights} ```ts title="src/api/middlewares.ts" highlights={highlights}
import { // TODO update import
import {
authenticate, authenticate,
requireCustomerAuthentication, } from "@medusajs/medusa/dist/utils/authenticate-middleware"
type MiddlewaresConfig,
} from "@medusajs/medusa"
export const config: MiddlewaresConfig = { export const config: MiddlewaresConfig = {
routes: [ routes: [
{ {
matcher: "/custom/admin*", matcher: "/custom/admin*",
middlewares: [authenticate()], middlewares: [
authenticate("admin", ["session", "bearer", "api-key"]),
],
}, },
{ {
matcher: "/custom/customer*", matcher: "/custom/customer*",
middlewares: [requireCustomerAuthentication()], middlewares: [
authenticate("store", ["session", "bearer"]),
],
}, },
], ],
} }
``` ```
The `authenticate` middleware function accepts three parameters:
1. The scope of authentication. Use `admin` for authenticating admin users, and `store` for authenticating customers.
2. An array of the types of authentication methods allowed. Both `admin` and `store` scopes support `session` and `bearer`. The `admin` scope also supports the `api-key` authentication method.
3. An optional object of options having the following properties:
1. `allowUnauthenticated`: (default: `false`) A boolean indicating whether authentication is required. For example, you may have an API route where you want to access the logged-in customer if available, but guest customers can still access it too. In that case, enable the `allowUnauthenticated` option.
2. `allowUnregistered`: (default: `false`) A boolean indicating whether new users can be authenticated.
@@ -77,7 +77,7 @@ const deletedRecords = await helloModuleService
.listMySoftDeletables({ .listMySoftDeletables({
// ... // ...
}, { }, {
withDeleted: true withDeleted: true,
}) })
``` ```
@@ -39,8 +39,6 @@ export const config: SubscriberConfig = {
This logs the product ID received in the `ProductService.Events.CREATED` (`product-created`) event’s data payload to the console. This logs the product ID received in the `ProductService.Events.CREATED` (`product-created`) event’s data payload to the console.
{/* TODO check first if the events list is still correct */} ## List of Events with Data Payload
{/* ## List of Events with Data Payload Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads.
Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */}
@@ -79,9 +79,9 @@ The `CustomProductData` data model has a `product_id` field to reference the pro
When you add a new data model, make sure to: When you add a new data model, make sure to:
- Create a migration for it. - [Create a migration for it.](../../../basics/data-models/page.mdx#create-a-migration)
- Add it to the second parameter of the main service's factory function. - [Add it to the second parameter of the main service's factory function.](../service-factory/page.mdx#abstractModuleServiceFactory-parameters)
- Add it to the container and connection loaders. - Add it to the [container](../container/page.mdx) and [connection](../connection-loader/page.mdx) loaders.
</Note> </Note>
@@ -120,8 +120,8 @@ class HelloModuleService extends ModulesSdkUtils
{ {
name: ["my_custom"], name: ["my_custom"],
args: { args: {
entity: MyCustom.name entity: MyCustom.name,
} },
}, },
{ {
name: ["custom_product_data"], name: ["custom_product_data"],
@@ -157,23 +157,23 @@ This creates a relationship to the `Product` data model of the Product Module us
name: "serviceName", name: "serviceName",
type: "`string`", type: "`string`",
optional: false, optional: false,
description: "The name of the module (as added in `medusa-config.js`)." description: "The name of your module (as added in `medusa-config.js`)."
}, },
{ {
name: "primaryKeys", name: "primaryKeys",
type: "`string[]`", type: "`string[]`",
optional: false, optional: false,
description: "The primary key field names used in the module's data models.", description: "The primary key field names used in your module's data models.",
}, },
{ {
name: "alias", name: "alias",
type: "`object[]`", type: "`object[]`",
description: "The alias definitions for each data model in the module. This allows other modules to reference your module's data models in relationships.", description: "The alias definitions for each data model in your module. This allows other modules to reference your module's data models in relationships.",
children: [ children: [
{ {
name: "name", name: "name",
type: "`string[]`", type: "`string[]`",
description: "The alias names used later when fetching or referencing the data in the model." description: "The alias names of a data model used later when fetching or referencing the data model."
}, },
{ {
name: "args", name: "args",
@@ -197,19 +197,19 @@ This creates a relationship to the `Product` data model of the Product Module us
{ {
name: "relationships", name: "relationships",
type: "`object[]`", type: "`object[]`",
description: "The module's relationships to other modules.", description: "Your module's relationships to other modules.",
children: [ children: [
{ {
name: "serviceName", name: "serviceName",
type: "`string`", type: "`string`",
optional: false, optional: false,
description: "The name of the module (as added in `medusa-config.js`) that this relationship is referencing. If you’re referencing a Medusa commerce module, use the `Modules` enum imported from `@medusajs/modules-sdk`." description: "The name of the module (as added in `medusa-config.js`) that this relationship is referencing. When referencing a Medusa commerce module, use the `Modules` enum imported from `@medusajs/modules-sdk`."
}, },
{ {
name: "alias", name: "alias",
type: "`string`", type: "`string`",
optional: false, optional: false,
description: "The alias of the data model you’re referencing in the other module. You can find it in the `__joinerConfig` method of the other module." description: "The alias of the data model you’re referencing in the other module. You can find it in the `__joinerConfig` method of the other module's service."
}, },
{ {
name: "primaryKey", name: "primaryKey",
@@ -70,7 +70,7 @@ class HelloModuleService extends ModulesSdkUtils
//... //...
this.options_ = moduleOptions || { this.options_ = moduleOptions || {
capitalize: false capitalize: false,
} }
} }
} }
@@ -98,14 +98,14 @@ const updateProduct = createStep(
return { return {
...variant, ...variant,
options: variantOptions options: variantOptions,
} }
}), }),
options: options.map((option) => ({ options: options.map((option) => ({
...option, ...option,
values: option.values.map((value) => value.value) values: option.values.map((value) => value.value),
})), })),
type_id: type.id type_id: type.id,
} }
) )
} }
+2 -1
View File
@@ -4,7 +4,7 @@ import { Inter, Roboto_Mono } from "next/font/google"
import Navbar from "@/components/Navbar" import Navbar from "@/components/Navbar"
import Providers from "@/providers" import Providers from "@/providers"
import "./globals.css" import "./globals.css"
import { TightLayout } from "docs-ui" import { Bannerv2, TightLayout } from "docs-ui"
import { config } from "@/config" import { config } from "@/config"
import clsx from "clsx" import clsx from "clsx"
import Feedback from "@/components/Feedback" import Feedback from "@/components/Feedback"
@@ -43,6 +43,7 @@ export default function RootLayout({
NavbarComponent={Navbar} NavbarComponent={Navbar}
sidebarProps={{ sidebarProps={{
expandItems: true, expandItems: true,
banner: <Bannerv2 />,
}} }}
showPagination={true} showPagination={true}
bodyClassName={clsx(inter.variable, robotoMono.variable)} bodyClassName={clsx(inter.variable, robotoMono.variable)}
+16 -5
View File
@@ -4,23 +4,34 @@ import {
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
formatReportLink, formatReportLink,
useIsBrowser,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { config } from "../../config" import { config } from "../../config"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname() const pathname = usePathname()
const isBrowser = useIsBrowser()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return ( return (
<UiFeedback <UiFeedback
event="survey" event="survey"
pathName={pathname} pathName={feedbackPathname}
reportLink={formatReportLink( reportLink={reportLink}
config.titleSuffix || "",
typeof document !== "undefined" ? document.title : ""
)}
question="Was this chapter helpful?" question="Was this chapter helpful?"
{...props} {...props}
/> />
+5 -3
View File
@@ -4,6 +4,7 @@ import { Navbar as UiNavbar, getNavbarItems } from "docs-ui"
import { useSidebar } from "docs-ui" import { useSidebar } from "docs-ui"
import { useMemo } from "react" import { useMemo } from "react"
import { config } from "../../config" import { config } from "../../config"
import { basePathUrl } from "../../utils/base-path-url"
const Navbar = () => { const Navbar = () => {
const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar() const { setMobileSidebarOpen, mobileSidebarOpen } = useSidebar()
@@ -12,7 +13,8 @@ const Navbar = () => {
() => () =>
getNavbarItems({ getNavbarItems({
basePath: config.baseUrl, basePath: config.baseUrl,
activePath: "/", activePath: basePathUrl(""),
version: "v2",
}), }),
[] []
) )
@@ -20,8 +22,8 @@ const Navbar = () => {
return ( return (
<UiNavbar <UiNavbar
logo={{ logo={{
light: "/images/logo-icon.png", light: basePathUrl("/images/logo-icon.png"),
dark: "/images/logo-icon-dark.png", dark: basePathUrl("/images/logo-icon-dark.png"),
}} }}
items={navbarItems} items={navbarItems}
mobileMenuButton={{ mobileMenuButton={{
+2 -1
View File
@@ -2,7 +2,8 @@ import { DocsConfig } from "types"
import { sidebarConfig } from "./sidebar" import { sidebarConfig } from "./sidebar"
export const config: DocsConfig = { export const config: DocsConfig = {
titleSuffix: "Medusa Book", titleSuffix: "Medusa v2 Docs",
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000", baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
sidebar: sidebarConfig, sidebar: sidebarConfig,
} }
+2 -2
View File
@@ -1,4 +1,4 @@
import { Badge, mobileSidebarItems } from "docs-ui" import { Badge, mobileSidebarItemsV2 } from "docs-ui"
import type { SidebarConfig, SidebarItemType } from "@/types" import type { SidebarConfig, SidebarItemType } from "@/types"
import { sidebar } from "../sidebar.mjs" import { sidebar } from "../sidebar.mjs"
@@ -20,5 +20,5 @@ const normalizeSidebarItems = (items: SidebarItemType[]) =>
export const sidebarConfig: SidebarConfig = { export const sidebarConfig: SidebarConfig = {
top: normalizeSidebarItems(sidebar), top: normalizeSidebarItems(sidebar),
bottom: [], bottom: [],
mobile: mobileSidebarItems, mobile: mobileSidebarItemsV2,
} }
+3 -27
View File
@@ -14,7 +14,6 @@ const withMDX = mdx({
extension: /\.mdx?$/, extension: /\.mdx?$/,
options: { options: {
rehypePlugins: [ rehypePlugins: [
// TODO add V2 to path if necessary
[ [
crossProjectLinksPlugin, crossProjectLinksPlugin,
{ {
@@ -25,13 +24,14 @@ const withMDX = mdx({
process.env.VERCEL_ENV !== "production" process.env.VERCEL_ENV !== "production"
? process.env.NEXT_PUBLIC_RESOURCES_URL ? process.env.NEXT_PUBLIC_RESOURCES_URL
: undefined, : undefined,
path: "resources", path: "v2/resources",
}, },
"user-guide": { "user-guide": {
url: url:
process.env.VERCEL_ENV !== "production" process.env.VERCEL_ENV !== "production"
? process.env.NEXT_PUBLIC_USER_GUIDE_URL ? process.env.NEXT_PUBLIC_USER_GUIDE_URL
: undefined, : undefined,
path: "v2/user-guide",
}, },
ui: { ui: {
url: url:
@@ -90,31 +90,7 @@ const nextConfig = {
pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"], pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"],
transpilePackages: ["docs-ui"], transpilePackages: ["docs-ui"],
// TODO uncomment if we decide on baes path basePath: process.env.NEXT_PUBLIC_BASE_PATH || "/v2",
// basePath: process.env.NEXT_PUBLIC_BASE_PATH || "/v2",
async rewrites() {
return {
fallback: [
{
source: "/resources",
destination: `${process.env.NEXT_PUBLIC_RESOURCES_URL}/resources`,
},
{
source: "/resources/:path*",
destination: `${process.env.NEXT_PUBLIC_RESOURCES_URL}/resources/:path*`,
},
// TODO comment out once we have the user guide published
// {
// source: "/user-guide",
// destination: `${process.env.NEXT_PUBLIC_USER_GUIDE_URL}/user-guide`,
// },
// {
// source: "/user-guide/:path*",
// destination: `${process.env.NEXT_PUBLIC_USER_GUIDE_URL}/user-guide/:path*`,
// },
],
}
},
} }
export default withMDX(nextConfig) export default withMDX(nextConfig)
+6 -32
View File
@@ -1,11 +1,6 @@
"use client" "use client"
import { import { SearchProvider as UiSearchProvider, searchFiltersV2 } from "docs-ui"
SearchProvider as UiSearchProvider,
AiAssistantCommandIcon,
AiAssistantProvider,
searchFilters,
} from "docs-ui"
import { config } from "../config" import { config } from "../config"
type SearchProviderProps = { type SearchProviderProps = {
@@ -32,15 +27,15 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
title: "Getting started? Try one of the following terms.", title: "Getting started? Try one of the following terms.",
items: [ items: [
"Install Medusa with create-medusa-app", "Install Medusa with create-medusa-app",
"What is a Service?",
"What is an API route?", "What is an API route?",
"What is a Module?",
"What is a Workflow?", "What is a Workflow?",
], ],
}, },
{ {
title: "Developing with Medusa", title: "Developing with Medusa",
items: [ items: [
"How to create a Service", "How to create a Module",
"How to create an API route", "How to create an API route",
"How to create a data model", "How to create a data model",
"How to create an admin widget", "How to create an admin widget",
@@ -48,32 +43,11 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
}, },
], ],
checkInternalPattern: new RegExp( checkInternalPattern: new RegExp(
`^${config.baseUrl}/([^(api|ui|resources)]/)*` `^${config.baseUrl}/v2/([^(resources)])*`
), ),
filterOptions: searchFilters, filterOptions: searchFiltersV2,
}} }}
initialDefaultFilters={["docs"]} initialDefaultFilters={["book"]}
commands={[
{
name: "ai-assistant",
icon: <AiAssistantCommandIcon />,
component: (
<AiAssistantProvider
apiUrl={process.env.NEXT_PUBLIC_AI_ASSISTANT_URL || "temp"}
websiteId={process.env.NEXT_PUBLIC_AI_WEBSITE_ID || "temp"}
recaptchaSiteKey={
process.env.NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY ||
"temp"
}
/>
),
title: "AI Assistant",
badge: {
variant: "purple",
children: "Beta",
},
},
]}
> >
{children} {children}
</UiSearchProvider> </UiSearchProvider>
+3
View File
@@ -0,0 +1,3 @@
export function basePathUrl(path = "") {
return `${process.env.NEXT_PUBLIC_BASE_PATH || ""}${path}`
}
@@ -1,5 +1,4 @@
import { Table } from "docs-ui" import { Table, TypeList } from "docs-ui"
import TypeList from "@/components/TypeList"
export const metadata = { export const metadata = {
title: `Medusa Admin Configurations`, title: `Medusa Admin Configurations`,
+6 -2
View File
@@ -175,7 +175,9 @@ The client accepts the following options on initialization:
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
A string indicating publishable API key used for storefront requests. You can create a publishable API key either using the [Medusa Admin](!user-guide!/settings/developer/api-key-management) or the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys). {/* TODO add link [Medusa Admin](!user-guide!/settings/developer/api-key-management) */}
A string indicating publishable API key used for storefront requests. You can create a publishable API key either using the Medusa Admin or the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys).
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
@@ -408,7 +410,9 @@ You can learn more about publishable API keys and how to use them in [this docum
### Create a Publishable API Key ### Create a Publishable API Key
You can create a publishable API key either using the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys), or using the [Medusa Admin](!user-guide!/settings/developer/api-key-management). {/* TODO add link [Medusa Admin](!user-guide!/settings/developer/api-key-management) */}
You can create a publishable API key either using the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys), or using the Medusa Admin.
### Use a Publishable API Key ### Use a Publishable API Key
+3 -1
View File
@@ -4,7 +4,7 @@ import { Inter, Roboto_Mono } from "next/font/google"
import Navbar from "@/components/Navbar" import Navbar from "@/components/Navbar"
import Providers from "@/providers" import Providers from "@/providers"
import "./globals.css" import "./globals.css"
import { TightLayout } from "docs-ui" import { Bannerv2, Breadcrumbs, TightLayout } from "docs-ui"
import { config } from "@/config" import { config } from "@/config"
import clsx from "clsx" import clsx from "clsx"
import { Feedback } from "@/components/Feedback" import { Feedback } from "@/components/Feedback"
@@ -44,9 +44,11 @@ export default function RootLayout({
NavbarComponent={Navbar} NavbarComponent={Navbar}
sidebarProps={{ sidebarProps={{
expandItems: true, expandItems: true,
banner: <Bannerv2 />,
}} }}
bodyClassName={clsx(inter.variable, robotoMono.variable)} bodyClassName={clsx(inter.variable, robotoMono.variable)}
> >
<Breadcrumbs />
{children} {children}
<Feedback className="my-2" /> <Feedback className="my-2" />
<EditButton /> <EditButton />
+3 -1
View File
@@ -327,7 +327,9 @@ You can learn more about publishable API keys and how to use them in [this docum
### Create a Publishable API Key ### Create a Publishable API Key
You can create a publishable API key either using the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys), or using the [Medusa Admin](!user-guide!/settings/developer/api-key-management). {/* TODO add link [Medusa Admin](!user-guide!/settings/developer/api-key-management) */}
You can create a publishable API key either using the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys), or using the Medusa Admin.
### Use a Publishable API Key ### Use a Publishable API Key
@@ -4,23 +4,34 @@ import {
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
formatReportLink, formatReportLink,
useIsBrowser,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { config } from "../../config" import { config } from "../../config"
import { useMemo } from "react"
import { basePathUrl } from "../../utils/base-path-url"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
export const Feedback = (props: FeedbackProps) => { export const Feedback = (props: FeedbackProps) => {
const pathname = usePathname() const pathname = usePathname()
const isBrowser = useIsBrowser()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return ( return (
<UiFeedback <UiFeedback
event="survey" event="survey"
pathName={pathname} pathName={feedbackPathname}
reportLink={formatReportLink( reportLink={reportLink}
config.titleSuffix || "",
typeof document !== "undefined" ? document.title : ""
)}
question="Was this page helpful?" question="Was this page helpful?"
{...props} {...props}
/> />
@@ -12,7 +12,8 @@ const Navbar = () => {
() => () =>
getNavbarItems({ getNavbarItems({
basePath: config.baseUrl, basePath: config.baseUrl,
activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/resources", activePath: basePathUrl(""),
version: "v2",
}), }),
[] []
) )
@@ -1,9 +0,0 @@
import { TypeList as UiTypeList } from "docs-ui"
import { ComponentProps } from "react"
import { config } from "@/config"
const TypeList = (props: ComponentProps<typeof UiTypeList>) => {
return <UiTypeList {...props} siteUrl={config.baseUrl} />
}
export default TypeList
+3 -2
View File
@@ -1,13 +1,14 @@
import { DocsConfig } from "types" import { DocsConfig } from "types"
import { mobileSidebarItems } from "docs-ui" import { mobileSidebarItemsV2 } from "docs-ui"
import { generatedSidebar } from "../generated/sidebar.mjs" import { generatedSidebar } from "../generated/sidebar.mjs"
export const config: DocsConfig = { export const config: DocsConfig = {
titleSuffix: "Medusa Resources", titleSuffix: "Medusa Resources",
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000", baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
sidebar: { sidebar: {
top: generatedSidebar, top: generatedSidebar,
bottom: [], bottom: [],
mobile: mobileSidebarItems, mobile: mobileSidebarItemsV2,
}, },
} }
+2 -2
View File
@@ -12,7 +12,6 @@ import remarkDirective from "remark-directive"
const mdxPluginOptions = { const mdxPluginOptions = {
options: { options: {
rehypePlugins: [ rehypePlugins: [
// TODO add V2 to path if necessary
[ [
crossProjectLinksPlugin, crossProjectLinksPlugin,
{ {
@@ -23,13 +22,14 @@ const mdxPluginOptions = {
process.env.VERCEL_ENV !== "production" process.env.VERCEL_ENV !== "production"
? process.env.NEXT_PUBLIC_DOCS_URL ? process.env.NEXT_PUBLIC_DOCS_URL
: undefined, : undefined,
path: "v2",
}, },
"user-guide": { "user-guide": {
url: url:
process.env.VERCEL_ENV !== "production" process.env.VERCEL_ENV !== "production"
? process.env.NEXT_PUBLIC_USER_GUIDE_URL ? process.env.NEXT_PUBLIC_USER_GUIDE_URL
: undefined, : undefined,
path: "user-guide", path: "v2/user-guide",
}, },
ui: { ui: {
url: url:
+1 -3
View File
@@ -28,9 +28,7 @@ const nextConfig = {
transpilePackages: ["docs-ui"], transpilePackages: ["docs-ui"],
// TODO uncomment if we decide on baes path basePath: process.env.NEXT_PUBLIC_BASE_PATH || "/v2/resources",
// basePath: process.env.NEXT_PUBLIC_BASE_PATH || "/v2/resources",
basePath: process.env.NEXT_PUBLIC_BASE_PATH || "/resources",
async rewrites() { async rewrites() {
return { return {
fallback: [ fallback: [
+4 -31
View File
@@ -1,11 +1,6 @@
"use client" "use client"
import { import { SearchProvider as UiSearchProvider, searchFiltersV2 } from "docs-ui"
SearchProvider as UiSearchProvider,
AiAssistantCommandIcon,
AiAssistantProvider,
searchFilters,
} from "docs-ui"
import { config } from "../config" import { config } from "../config"
type SearchProviderProps = { type SearchProviderProps = {
@@ -39,32 +34,10 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
], ],
}, },
], ],
checkInternalPattern: new RegExp(`^${config.baseUrl}/resources/.*`), checkInternalPattern: new RegExp(`^${config.baseUrl}/v2/resources/.*`),
filterOptions: searchFilters, filterOptions: searchFiltersV2,
}} }}
// TODO change once we have a dedicated index initialDefaultFilters={["resources"]}
initialDefaultFilters={["docs"]}
commands={[
{
name: "ai-assistant",
icon: <AiAssistantCommandIcon />,
component: (
<AiAssistantProvider
apiUrl={process.env.NEXT_PUBLIC_AI_ASSISTANT_URL || "temp"}
websiteId={process.env.NEXT_PUBLIC_AI_WEBSITE_ID || "temp"}
recaptchaSiteKey={
process.env.NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY ||
"temp"
}
/>
),
title: "AI Assistant",
badge: {
variant: "purple",
children: "Beta",
},
},
]}
> >
{children} {children}
</UiSearchProvider> </UiSearchProvider>
+2 -1
View File
@@ -8,4 +8,5 @@ NEXT_PUBLIC_ALGOLIA_APP_ID=
NEXT_PUBLIC_SEGMENT_API_KEY= NEXT_PUBLIC_SEGMENT_API_KEY=
NEXT_PUBLIC_AI_ASSISTANT_URL= NEXT_PUBLIC_AI_ASSISTANT_URL=
NEXT_PUBLIC_AI_WEBSITE_ID= NEXT_PUBLIC_AI_WEBSITE_ID=
NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY= NEXT_PUBLIC_AI_API_ASSISTANT_RECAPTCHA_SITE_KEY=
NEXT_PUBLIC_SHOW_V2=
+11 -3
View File
@@ -1,10 +1,15 @@
import { ArrowUpRightOnBox } from "@medusajs/icons" import { ArrowUpRightOnBox } from "@medusajs/icons"
import { NavbarLinkProps, getNavbarItems, mobileSidebarItems } from "docs-ui" import {
NavbarItem,
getNavbarItems,
legacyMobileSidebarItems,
mobileSidebarItemsV1,
} from "docs-ui"
import { SidebarSectionItemsType } from "types" import { SidebarSectionItemsType } from "types"
import { siteConfig } from "./site" import { siteConfig } from "./site"
type DocsConfig = { type DocsConfig = {
mainNav: NavbarLinkProps[] mainNav: NavbarItem[]
sidebar: SidebarSectionItemsType sidebar: SidebarSectionItemsType
} }
@@ -12,6 +17,7 @@ export const docsConfig: DocsConfig = {
mainNav: getNavbarItems({ mainNav: getNavbarItems({
basePath: siteConfig.baseUrl, basePath: siteConfig.baseUrl,
activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/ui", activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/ui",
version: process.env.NEXT_PUBLIC_SHOW_V2 ? "v1" : "legacy",
}), }),
sidebar: { sidebar: {
top: [ top: [
@@ -279,6 +285,8 @@ export const docsConfig: DocsConfig = {
], ],
}, },
], ],
mobile: mobileSidebarItems, mobile: process.env.NEXT_PUBLIC_SHOW_V2
? mobileSidebarItemsV1
: legacyMobileSidebarItems,
}, },
} }
+1
View File
@@ -11,6 +11,7 @@ const baseUrl = process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3000"
export const siteConfig: SiteConfig = { export const siteConfig: SiteConfig = {
name: "Medusa UI", name: "Medusa UI",
baseUrl, baseUrl,
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
url: `${baseUrl}/${process.env.NEXT_PUBLIC_BASE_PATH}`, url: `${baseUrl}/${process.env.NEXT_PUBLIC_BASE_PATH}`,
description: "Primitives for building Medusa applications.", description: "Primitives for building Medusa applications.",
// sidebar is defined in docs.tsx // sidebar is defined in docs.tsx
+1 -1
View File
@@ -1,3 +1,3 @@
export function basePathUrl(path = "") { export function basePathUrl(path = "") {
return `${process.env.NEXT_PUBLIC_BASE_PATH}${path}` return `${process.env.NEXT_PUBLIC_BASE_PATH || ""}${path}`
} }
+2 -2
View File
@@ -4,7 +4,7 @@ import {
AiAssistantCommandIcon, AiAssistantCommandIcon,
AiAssistantProvider, AiAssistantProvider,
SearchProvider as UiSearchProvider, SearchProvider as UiSearchProvider,
searchFilters, searchFiltersV1,
} from "docs-ui" } from "docs-ui"
import { absoluteUrl } from "../lib/absolute-url" import { absoluteUrl } from "../lib/absolute-url"
@@ -34,7 +34,7 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
}, },
], ],
checkInternalPattern: new RegExp(`^${absoluteUrl()}/ui`), checkInternalPattern: new RegExp(`^${absoluteUrl()}/ui`),
filterOptions: searchFilters, filterOptions: searchFiltersV1,
}} }}
initialDefaultFilters={["ui"]} initialDefaultFilters={["ui"]}
commands={[ commands={[
@@ -4,23 +4,34 @@ import {
Feedback as UiFeedback, Feedback as UiFeedback,
FeedbackProps as UiFeedbackProps, FeedbackProps as UiFeedbackProps,
formatReportLink, formatReportLink,
useIsBrowser,
} from "docs-ui" } from "docs-ui"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { config } from "../../config" import { config } from "../../config"
import { basePathUrl } from "../../utils/base-path-url"
import { useMemo } from "react"
type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName"> type FeedbackProps = Omit<UiFeedbackProps, "event" | "pathName">
const Feedback = (props: FeedbackProps) => { const Feedback = (props: FeedbackProps) => {
const pathname = usePathname() const pathname = usePathname()
const isBrowser = useIsBrowser()
const feedbackPathname = useMemo(() => basePathUrl(pathname), [pathname])
const reportLink = useMemo(
() =>
formatReportLink(
config.titleSuffix || "",
isBrowser ? document.title : ""
),
[isBrowser]
)
return ( return (
<UiFeedback <UiFeedback
event="survey" event="survey"
pathName={pathname} pathName={feedbackPathname}
reportLink={formatReportLink( reportLink={reportLink}
config.titleSuffix || "",
typeof document !== "undefined" ? document.title : ""
)}
question="Was this chapter helpful?" question="Was this chapter helpful?"
{...props} {...props}
/> />
@@ -14,6 +14,7 @@ const Navbar = () => {
getNavbarItems({ getNavbarItems({
basePath: config.baseUrl, basePath: config.baseUrl,
activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/user-guide", activePath: process.env.NEXT_PUBLIC_BASE_PATH || "/user-guide",
version: "v2",
}), }),
[] []
) )
+3 -2
View File
@@ -1,13 +1,14 @@
import { DocsConfig } from "types" import { DocsConfig } from "types"
import { mobileSidebarItems } from "docs-ui" import { mobileSidebarItemsV2 } from "docs-ui"
import { generatedSidebar as sidebar } from "@/generated/sidebar.mjs" import { generatedSidebar as sidebar } from "@/generated/sidebar.mjs"
export const config: DocsConfig = { export const config: DocsConfig = {
titleSuffix: "Medusa Admin User Guide", titleSuffix: "Medusa Admin User Guide",
baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000", baseUrl: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
sidebar: { sidebar: {
top: sidebar, top: sidebar,
bottom: [], bottom: [],
mobile: mobileSidebarItems, mobile: mobileSidebarItemsV2,
}, },
} }
+2 -2
View File
@@ -4,7 +4,7 @@ import {
SearchProvider as UiSearchProvider, SearchProvider as UiSearchProvider,
AiAssistantCommandIcon, AiAssistantCommandIcon,
AiAssistantProvider, AiAssistantProvider,
searchFilters, searchFiltersV1,
} from "docs-ui" } from "docs-ui"
import { config } from "../config" import { config } from "../config"
@@ -42,7 +42,7 @@ const SearchProvider = ({ children }: SearchProviderProps) => {
}, },
], ],
checkInternalPattern: new RegExp(`^${config.baseUrl}/user-guide`), checkInternalPattern: new RegExp(`^${config.baseUrl}/user-guide`),
filterOptions: searchFilters, filterOptions: searchFiltersV1,
}} }}
initialDefaultFilters={["user-guide"]} initialDefaultFilters={["user-guide"]}
commands={[ commands={[
@@ -0,0 +1,15 @@
import React from "react"
import { Card, Link } from "../.."
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>
)
}
@@ -0,0 +1,76 @@
"use client"
import React, { useMemo } from "react"
import { CurrentItemsState, useSidebar } from "@/providers"
import { TriangleRightMini } from "@medusajs/icons"
import Link from "next/link"
import clsx from "clsx"
type BreadcrumbItem = Map<string, string>
export const Breadcrumbs = () => {
const { currentItems } = useSidebar()
const getBreadcrumbsOfItem = (item: CurrentItemsState): BreadcrumbItem => {
let tempBreadcrumbItems: BreadcrumbItem = new Map()
if (item.previousSidebar) {
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
}
tempBreadcrumbItems.set(
item.parentItem?.path || item.top[0].path || "/",
item.parentItem?.childSidebarTitle || item.parentItem?.title || ""
)
return tempBreadcrumbItems
}
const breadcrumbItems = useMemo(() => {
const tempBreadcrumbItems: BreadcrumbItem = new Map()
if (!currentItems) {
return tempBreadcrumbItems
}
tempBreadcrumbItems.set("/", "Home")
getBreadcrumbsOfItem(currentItems).forEach((value, key) =>
tempBreadcrumbItems.set(key, value)
)
return tempBreadcrumbItems
}, [currentItems])
const getBreadcrumbItemElms = (): React.ReactNode[] => {
const elms: React.ReactNode[] = []
breadcrumbItems.forEach((title, path) => {
elms.push(
<React.Fragment key={path}>
{elms.length !== 0 && (
<TriangleRightMini className={clsx("text-medusa-fg-muted")} />
)}
<Link
href={path}
className={clsx(
"text-compact-x-small-plus text-medusa-fg-muted",
"hover:text-medusa-fg-subtle transition-colors"
)}
>
{title}
</Link>
</React.Fragment>
)
})
return elms
}
return (
<>
{breadcrumbItems.size > 0 && (
<div className="flex gap-docs_0.25 items-center mb-docs_1">
{...getBreadcrumbItemElms()}
</div>
)}
</>
)
}
@@ -2,7 +2,7 @@
import React from "react" import React from "react"
import clsx from "clsx" import clsx from "clsx"
import { Link, LinkProps } from "@/components" import { Badge, BadgeProps, Link, LinkProps } from "@/components"
export type NavbarLinkProps = { export type NavbarLinkProps = {
href: string href: string
@@ -10,6 +10,7 @@ export type NavbarLinkProps = {
className?: string className?: string
activeValuePattern?: RegExp activeValuePattern?: RegExp
isActive?: boolean isActive?: boolean
badge?: BadgeProps
} & LinkProps } & LinkProps
export const NavbarLink = ({ export const NavbarLink = ({
@@ -17,6 +18,7 @@ export const NavbarLink = ({
label, label,
className, className,
isActive, isActive,
badge
}: NavbarLinkProps) => { }: NavbarLinkProps) => {
return ( return (
<Link <Link
@@ -30,6 +32,10 @@ export const NavbarLink = ({
)} )}
> >
{label} {label}
{badge && <Badge {...badge} className={clsx(
badge.className,
"ml-docs_0.5"
)} />}
</Link> </Link>
) )
} }
@@ -4,6 +4,7 @@ import React from "react"
import { useColorMode } from "@/providers" import { useColorMode } from "@/providers"
import Link from "next/link" import Link from "next/link"
import clsx from "clsx" import clsx from "clsx"
import Image from "next/image"
export type NavbarLogoProps = { export type NavbarLogoProps = {
light: string light: string
@@ -22,7 +23,7 @@ export const NavbarLogo = ({
return ( return (
<Link href={`/`} className={clsx("flex-1", className)}> <Link href={`/`} className={clsx("flex-1", className)}>
<img <Image
src={colorMode === "light" ? light : dark || light} src={colorMode === "light" ? light : dark || light}
alt="Medusa Logo" alt="Medusa Logo"
height={20} height={20}
@@ -0,0 +1,11 @@
import clsx from "clsx"
import React from "react"
export const NavbarDivider = () => {
return (
<div className={clsx(
"w-px h-1/2 bg-medusa-fg-disabled",
"mx-docs_0.5"
)}></div>
)
}
@@ -6,10 +6,19 @@ import { NavbarLogo, NavbarLogoProps } from "./Logo"
import { NavbarMobileMenu } from "./MobileMenu" import { NavbarMobileMenu } from "./MobileMenu"
import { NavbarSearchModalOpener } from "./SearchModalOpener" import { NavbarSearchModalOpener } from "./SearchModalOpener"
import { NavbarMobileMenuButtonProps } from "./MobileMenu/Button" import { NavbarMobileMenuButtonProps } from "./MobileMenu/Button"
import { NavbarDivider } from "./NavbarDivider"
export type NavbarItem = {
type: "link"
props: NavbarLinkProps
} | {
type: "divider"
props?: {}
}
export type NavbarProps = { export type NavbarProps = {
logo: NavbarLogoProps logo: NavbarLogoProps
items: NavbarLinkProps[] items: NavbarItem[]
showSearchOpener?: boolean showSearchOpener?: boolean
showColorModeToggle?: boolean showColorModeToggle?: boolean
additionalActionsAfter?: React.ReactNode additionalActionsAfter?: React.ReactNode
@@ -45,9 +54,14 @@ export const Navbar = ({
> >
<div className="hidden w-full items-center gap-docs_0.5 lg:flex lg:w-auto lg:gap-docs_1.5"> <div className="hidden w-full items-center gap-docs_0.5 lg:flex lg:w-auto lg:gap-docs_1.5">
<NavbarLogo {...logo} /> <NavbarLogo {...logo} />
{items.map((item, index) => ( {items.map(({type, props}, index) => {
<NavbarLink key={index} {...item} /> switch(type) {
))} case "divider":
return <NavbarDivider key={index} />
default:
return <NavbarLink key={index} {...props} />
}
})}
</div> </div>
<div className="hidden min-w-0 flex-1 items-center justify-end gap-docs_0.5 lg:flex"> <div className="hidden min-w-0 flex-1 items-center justify-end gap-docs_0.5 lg:flex">
{additionalActionsBefore} {additionalActionsBefore}
@@ -13,6 +13,7 @@ export const Pagination = () => {
{previousPage && ( {previousPage && (
<Card <Card
title={previousPage.title} title={previousPage.title}
text={previousPage.parentTitle}
startIcon={<ChevronLeft />} startIcon={<ChevronLeft />}
showLinkIcon={false} showLinkIcon={false}
href={previousPage.link} href={previousPage.link}
@@ -22,6 +23,7 @@ export const Pagination = () => {
{nextPage && ( {nextPage && (
<Card <Card
title={nextPage.title} title={nextPage.title}
text={nextPage.parentTitle}
endIcon={<ChevronRight />} endIcon={<ChevronRight />}
showLinkIcon={false} showLinkIcon={false}
href={nextPage.link} href={nextPage.link}
@@ -85,6 +85,9 @@ export const Search = ({
<InstantSearch <InstantSearch
indexName={algolia.mainIndexName} indexName={algolia.mainIndexName}
searchClient={searchClient} searchClient={searchClient}
future={{
preserveSharedStateOnUnmount: true
}}
> >
<div className={clsx("bg-medusa-bg-base flex")}> <div className={clsx("bg-medusa-bg-base flex")}>
<SearchBox <SearchBox
@@ -11,7 +11,7 @@ export const SidebarBack = () => {
return ( return (
<div <div
className={clsx( className={clsx(
"my-docs_1.5 cursor-pointer", "mb-docs_1.5 cursor-pointer",
"flex items-center gap-docs_0.5 rounded-docs_sm px-docs_0.5 py-[6px] hover:no-underline", "flex items-center gap-docs_0.5 rounded-docs_sm px-docs_0.5 py-[6px] hover:no-underline",
"border border-transparent", "border border-transparent",
"text-medusa-fg-subtle text-medium-plus" "text-medusa-fg-subtle text-medium-plus"
@@ -16,6 +16,8 @@ export type SidebarItemProps = {
expandItems?: boolean expandItems?: boolean
currentLevel?: number currentLevel?: number
isSidebarTitle?: boolean isSidebarTitle?: boolean
sidebarHasParent?: boolean
isMobile?: boolean
} & React.AllHTMLAttributes<HTMLLIElement> } & React.AllHTMLAttributes<HTMLLIElement>
export const SidebarItem = ({ export const SidebarItem = ({
@@ -24,6 +26,8 @@ export const SidebarItem = ({
expandItems = false, expandItems = false,
className, className,
currentLevel = 1, currentLevel = 1,
sidebarHasParent = false,
isMobile = false
}: SidebarItemProps) => { }: SidebarItemProps) => {
const [showLoading, setShowLoading] = useState(false) const [showLoading, setShowLoading] = useState(false)
const { const {
@@ -34,8 +38,8 @@ export const SidebarItem = ({
sidebarRef, sidebarRef,
} = useSidebar() } = useSidebar()
const active = useMemo( const active = useMemo(
() => isItemActive(item, nested), () => !isMobile && isItemActive(item, nested),
[isItemActive, item, nested] [isItemActive, item, nested, isMobile]
) )
const collapsed = !expandItems && !isItemActive(item, true) const collapsed = !expandItems && !isItemActive(item, true)
const ref = useRef<HTMLLIElement>(null) const ref = useRef<HTMLLIElement>(null)
@@ -131,7 +135,11 @@ export const SidebarItem = ({
return ( return (
<li <li
className={clsx( className={clsx(
canHaveTitleStyling && !collapsed && "my-docs_1.5", canHaveTitleStyling &&
!collapsed && [
!sidebarHasParent && "my-docs_1.5",
sidebarHasParent && "[&:not(:first-child)]:my-docs_1.5",
],
!canHaveTitleStyling && !canHaveTitleStyling &&
!nested && !nested &&
active && active &&
@@ -11,9 +11,9 @@ export const SidebarTitle = ({ item }: SidebarTitleProps) => {
return ( return (
<Link <Link
className={clsx( className={clsx(
"flex items-center justify-between gap-docs_0.5 rounded-docs_sm px-docs_0.5 py-[6px] hover:no-underline", "flex items-center justify-between gap-docs_0.5 rounded-docs_sm px-docs_0.5 pb-[6px] hover:no-underline",
"border border-transparent", "border border-transparent",
"text-medusa-fg-subtle text-medium-plus" "text-medusa-fg-subtle text-large-plus"
)} )}
href={item.isPathHref && item.path ? item.path : `#${item.path}`} href={item.isPathHref && item.path ? item.path : `#${item.path}`}
replace={!item.isPathHref} replace={!item.isPathHref}
@@ -1,6 +1,6 @@
"use client" "use client"
import React, { useMemo } from "react" import React, { useMemo, useRef } from "react"
import { useSidebar } from "@/providers" import { useSidebar } from "@/providers"
import clsx from "clsx" import clsx from "clsx"
import { Loading } from "@/components" import { Loading } from "@/components"
@@ -12,11 +12,13 @@ import { CSSTransition, SwitchTransition } from "react-transition-group"
export type SidebarProps = { export type SidebarProps = {
className?: string className?: string
expandItems?: boolean expandItems?: boolean
banner?: React.ReactNode
} }
export const Sidebar = ({ export const Sidebar = ({
className = "", className = "",
expandItems = false, expandItems = false,
banner,
}: SidebarProps) => { }: SidebarProps) => {
const { const {
items, items,
@@ -32,6 +34,11 @@ export const Sidebar = ({
[items, currentItems] [items, currentItems]
) )
const sidebarHasParent = useMemo(
() => sidebarItems.parentItem !== undefined,
[sidebarItems]
)
return ( return (
<aside <aside
className={clsx( className={clsx(
@@ -47,69 +54,76 @@ export const Sidebar = ({
animationFillMode: "forwards", animationFillMode: "forwards",
}} }}
> >
<SwitchTransition> <ul
<CSSTransition className={clsx(
key={sidebarItems.parentItem?.title || "home"} "sticky top-0 h-screen max-h-screen w-full list-none p-0",
nodeRef={sidebarRef} "px-docs_1.5 pb-[57px] pt-docs_1.5",
classNames={{ "flex flex-col"
enter: "animate-fadeInLeft animate-fast", )}
exit: "animate-fadeOutLeft animate-fast", id="sidebar"
}} >
timeout={200} {banner && <div className="mb-docs_1">{banner}</div>}
> {sidebarItems.parentItem && (
<ul <div className={clsx("mb-docs_1", !banner && "mt-docs_1.5")}>
className={clsx( <SidebarBack />
"sticky top-0 h-screen max-h-screen w-full list-none overflow-auto p-0", <SidebarTitle item={sidebarItems.parentItem} />
"px-docs_1.5 pb-[57px] pt-docs_1.5" </div>
)} )}
id="sidebar" <SwitchTransition>
ref={sidebarRef} <CSSTransition
key={sidebarItems.parentItem?.title || "home"}
nodeRef={sidebarRef}
classNames={{
enter: "animate-fadeInLeft animate-fast",
exit: "animate-fadeOutLeft animate-fast",
}}
timeout={200}
> >
{sidebarItems.parentItem && ( <div className="overflow-auto" ref={sidebarRef}>
<> <div className={clsx("mb-docs_1.5 lg:hidden")}>
<SidebarBack /> {!sidebarItems.mobile.length && !staticSidebarItems && (
<SidebarTitle item={sidebarItems.parentItem} /> <Loading className="px-0" />
</> )}
)} {sidebarItems.mobile.map((item, index) => (
<div className="mb-docs_1.5 lg:hidden"> <SidebarItem
{!sidebarItems.mobile.length && !staticSidebarItems && ( item={item}
<Loading className="px-0" /> key={index}
)} expandItems={expandItems}
{sidebarItems.mobile.map((item, index) => ( sidebarHasParent={sidebarHasParent}
<SidebarItem isMobile={true}
item={item} />
key={index} ))}
expandItems={expandItems} </div>
/> <div className={clsx("mb-docs_1.5")}>
))} {!sidebarItems.top.length && !staticSidebarItems && (
<Loading className="px-0" />
)}
{sidebarItems.top.map((item, index) => (
<SidebarItem
item={item}
key={index}
expandItems={expandItems}
sidebarHasParent={sidebarHasParent}
/>
))}
</div>
<div className="mb-docs_1.5">
{!sidebarItems.bottom.length && !staticSidebarItems && (
<Loading className="px-0" />
)}
{sidebarItems.bottom.map((item, index) => (
<SidebarItem
item={item}
key={index}
expandItems={expandItems}
sidebarHasParent={sidebarHasParent}
/>
))}
</div>
</div> </div>
<div className="mb-docs_1.5"> </CSSTransition>
{!sidebarItems.top.length && !staticSidebarItems && ( </SwitchTransition>
<Loading className="px-0" /> </ul>
)}
{sidebarItems.top.map((item, index) => (
<SidebarItem
item={item}
key={index}
expandItems={expandItems}
/>
))}
</div>
<div className="mb-docs_1.5">
{!sidebarItems.bottom.length && !staticSidebarItems && (
<Loading className="px-0" />
)}
{sidebarItems.bottom.map((item, index) => (
<SidebarItem
item={item}
key={index}
expandItems={expandItems}
/>
))}
</div>
</ul>
</CSSTransition>
</SwitchTransition>
</aside> </aside>
) )
} }
@@ -21,7 +21,7 @@ import {
} from "@medusajs/icons" } from "@medusajs/icons"
import { decodeStr, isInView } from "@/utils" import { decodeStr, isInView } from "@/utils"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useIsBrowser } from "../../.." import { useIsBrowser, useSiteConfig } from "../../.."
type CommonProps = ParentCommonProps & { type CommonProps = ParentCommonProps & {
level?: number level?: number
@@ -39,10 +39,11 @@ const TypeListItem = ({
expandUrl, expandUrl,
elementKey, elementKey,
sectionTitle, sectionTitle,
siteUrl = "",
}: TypeListItemProps) => { }: TypeListItemProps) => {
const isBrowser = useIsBrowser() const isBrowser = useIsBrowser()
const pathname = usePathname() const pathname = usePathname()
const { config: { baseUrl, basePath } } = useSiteConfig()
const siteUrl = `${baseUrl}${basePath}`
const groupName = useMemo(() => { const groupName = useMemo(() => {
switch (level) { switch (level) {
@@ -5,7 +5,6 @@ import { Loading } from "@/components"
export type CommonProps = { export type CommonProps = {
expandUrl?: string expandUrl?: string
sectionTitle?: string sectionTitle?: string
siteUrl?: string
} }
export type Type = { export type Type = {
@@ -30,7 +29,6 @@ const TypeListItems = lazy(async () => import("./Items"))
export const TypeList = ({ export const TypeList = ({
types, types,
className, className,
siteUrl,
sectionTitle, sectionTitle,
expandUrl, expandUrl,
...props ...props
@@ -48,7 +46,6 @@ export const TypeList = ({
types={types} types={types}
expandUrl={expandUrl} expandUrl={expandUrl}
sectionTitle={sectionTitle} sectionTitle={sectionTitle}
siteUrl={siteUrl}
/> />
</Suspense> </Suspense>
</div> </div>
@@ -1,8 +1,10 @@
export * from "./AiAssistant" export * from "./AiAssistant"
export * from "./AiAssistant/CommandIcon" export * from "./AiAssistant/CommandIcon"
export * from "./Badge" export * from "./Badge"
export * from "./Bannerv2"
export * from "./Bordered" export * from "./Bordered"
export * from "./BorderedIcon" export * from "./BorderedIcon"
export * from "./Breadcrumbs"
export * from "./Button" export * from "./Button"
export * from "./Card" export * from "./Card"
export * from "./CardList" export * from "./CardList"
-198
View File
@@ -1,198 +0,0 @@
import { NavbarLinkProps } from "@/components"
import { OptionType } from "./hooks"
import { SidebarItemType } from "types"
export const GITHUB_ISSUES_PREFIX = `https://github.com/medusajs/medusa/issues/new?assignees=&labels=type%3A+docs&template=docs.yml`
export const GITHUB_UI_ISSUES_PREFIX = `https://github.com/medusajs/ui/issues/new?labels=documentation`
export const navbarItems: NavbarLinkProps[] = [
{
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/`,
},
{
label: "Resources",
target: "_blank",
rel: "noreferrer",
href: `/resources`,
},
{
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/user-guide`,
},
{
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
{
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
{
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
},
]
/**
* TODO Uncomment for V2
*
* export const navbarItems: NavbarLinkProps[] = [
{
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/`,
activeValuePattern: /^\/(?!api\/|ui\/|user-guide\/|resources\/)/,
},
{
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/user-guide`,
},
{
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
{
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
{
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
activeValuePattern: /^\/ui\//,
},
{
label: "V2 Docs",
target: "_blank",
rel: "noreferrer",
href: `/v2`,
},
]
export const navbarItemsV2: NavbarLinkProps[] = [
{
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/v2`,
activeValuePattern: /^\/v2\/(?!api\/|ui\/|user-guide\/|resources\/)/,
},
{
label: "Resources",
target: "_blank",
rel: "noreferrer",
href: `/v2/resources`,
activeValuePattern: /^\/v2\/resources\//,
},
{
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/v2/user-guide`,
activeValuePattern: /^\/v2\/user-guide/,
},
{
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
{
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
{
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
activeValuePattern: /^\/ui\//,
},
]
*/
export const mobileSidebarItems: SidebarItemType[] = [
{
title: "Docs",
path: `/`,
loaded: true,
isPathHref: true,
},
{
title: "User Guide",
path: `/user-guide`,
loaded: true,
isPathHref: true,
},
{
title: "Store API",
path: `/api/store`,
loaded: true,
isPathHref: true,
},
{
title: "Admin API",
path: `/api/admin`,
loaded: true,
isPathHref: true,
},
{
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
]
// TODO add resources once we create index
export const searchFilters: OptionType[] = [
{
value: "admin",
label: "Admin API",
},
{
value: "store",
label: "Store API",
},
{
value: "docs",
label: "Docs",
},
{
value: "user-guide",
label: "User Guide",
},
{
value: "plugins",
label: "Plugins",
},
{
value: "reference",
label: "References",
},
{
value: "ui",
label: "UI",
},
]
+368
View File
@@ -0,0 +1,368 @@
import React from "react"
import { Badge, NavbarItem } from "@/components"
import { OptionType } from "./hooks"
import { SidebarItemType } from "types"
export const GITHUB_ISSUES_PREFIX = `https://github.com/medusajs/medusa/issues/new?assignees=&labels=type%3A+docs&template=docs.yml`
export const GITHUB_UI_ISSUES_PREFIX = `https://github.com/medusajs/ui/issues/new?labels=documentation`
export const navbarItemsV1: NavbarItem[] = [
{
type: "link",
props: {
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/`,
},
},
{
type: "link",
props: {
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/user-guide`,
},
},
{
type: "link",
props: {
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
},
{
type: "link",
props: {
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
},
{
type: "link",
props: {
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
},
},
{
type: "divider",
},
{
type: "link",
props: {
label: "Learn Medusa v2",
target: "_blank",
rel: "noreferrer",
href: `/v2`,
badge: {
variant: "blue",
children: "New",
},
},
},
]
export const navbarItemsV2: NavbarItem[] = [
{
type: "link",
props: {
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/v2`,
},
},
{
type: "link",
props: {
label: "Learning Resources",
target: "_blank",
rel: "noreferrer",
href: `/v2/resources`,
},
},
{
type: "divider",
},
{
type: "link",
props: {
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/`,
badge: {
variant: "neutral",
children: "v1",
},
},
},
{
type: "link",
props: {
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/user-guide`,
badge: {
variant: "neutral",
children: "v1",
},
},
},
{
type: "link",
props: {
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
},
{
type: "link",
props: {
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
},
{
type: "link",
props: {
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
},
},
]
export const legacyNavbarItems: NavbarItem[] = [
{
type: "link",
props: {
label: "Docs",
target: "_blank",
rel: "noreferrer",
href: `/`,
},
},
{
type: "link",
props: {
label: "Resources",
target: "_blank",
rel: "noreferrer",
href: `/resources`,
},
},
{
type: "link",
props: {
label: "User Guide",
target: "_blank",
rel: "noreferrer",
href: `/user-guide`,
},
},
{
type: "link",
props: {
label: "Store API",
target: "_blank",
rel: "noreferrer",
href: `/api/store`,
},
},
{
type: "link",
props: {
label: "Admin API",
target: "_blank",
rel: "noreferrer",
href: `/api/admin`,
},
},
{
type: "link",
props: {
label: "UI",
target: "_blank",
rel: "noreferrer",
href: `/ui`,
},
},
]
export const mobileSidebarItemsV1: SidebarItemType[] = [
{
title: "Docs",
path: `/`,
loaded: true,
isPathHref: true,
},
{
title: "User Guide",
path: `/user-guide`,
loaded: true,
isPathHref: true,
},
{
title: "Store API",
path: `/api/store`,
loaded: true,
isPathHref: true,
},
{
title: "Admin API",
path: `/api/admin`,
loaded: true,
isPathHref: true,
},
{
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
{
title: "Book",
path: `/v2`,
loaded: true,
isPathHref: true,
additionalElms: <Badge variant="blue">v2</Badge>,
},
]
export const mobileSidebarItemsV2: SidebarItemType[] = [
{
title: "Book",
path: `/v2`,
loaded: true,
isPathHref: true,
},
{
title: "Learning Resources",
path: `/v2/resources`,
loaded: true,
isPathHref: true,
},
{
title: "Docs",
path: `/`,
loaded: true,
isPathHref: true,
additionalElms: <Badge variant="neutral">v1</Badge>,
},
{
title: "User Guide",
path: `/user-guide`,
loaded: true,
isPathHref: true,
additionalElms: <Badge variant="neutral">v1</Badge>,
},
{
title: "Store API",
path: `/api/store`,
loaded: true,
isPathHref: true,
},
{
title: "Admin API",
path: `/api/admin`,
loaded: true,
isPathHref: true,
},
{
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
]
export const legacyMobileSidebarItems: SidebarItemType[] = [
{
title: "Docs",
path: `/`,
loaded: true,
isPathHref: true,
},
{
title: "User Guide",
path: `/user-guide`,
loaded: true,
isPathHref: true,
},
{
title: "Store API",
path: `/api/store`,
loaded: true,
isPathHref: true,
},
{
title: "Admin API",
path: `/api/admin`,
loaded: true,
isPathHref: true,
},
{
title: "UI",
path: `/ui`,
loaded: true,
isPathHref: true,
},
]
export const searchFiltersV2: OptionType[] = [
{
value: "book",
label: "Book",
},
{
value: "resources",
label: "Resources",
},
// TODO add more filters once V2 become the main docs
]
export const searchFiltersV1: OptionType[] = [
{
value: "admin",
label: "Admin API",
},
{
value: "store",
label: "Store API",
},
{
value: "docs",
label: "Docs",
},
{
value: "user-guide",
label: "User Guide",
},
{
value: "plugins",
label: "Plugins",
},
{
value: "reference",
label: "References",
},
{
value: "ui",
label: "UI",
},
]
@@ -14,6 +14,7 @@ import { SidebarItemType } from "types"
export type Page = { export type Page = {
title: string title: string
description?: string description?: string
parentTitle?: string
link: string link: string
} }
@@ -26,10 +27,14 @@ export const PaginationContext = createContext<PaginationContextType | null>(
null null
) )
type SidebarItemWithParent = SidebarItemType & {
parent?: SidebarItemType
}
type SearchItemsResult = { type SearchItemsResult = {
foundActive: boolean foundActive: boolean
prevItem?: SidebarItemType prevItem?: SidebarItemWithParent
nextItem?: SidebarItemType nextItem?: SidebarItemWithParent
} }
export type PaginationProviderProps = { export type PaginationProviderProps = {
@@ -66,14 +71,20 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
const getPrevItem = ( const getPrevItem = (
items: SidebarItemType[], items: SidebarItemType[],
index: number index: number
): SidebarItemType | undefined => { ): SidebarItemWithParent | undefined => {
let foundItem: SidebarItemType | undefined let foundItem: SidebarItemWithParent | undefined
items items
.slice(0, index) .slice(0, index)
.reverse() .reverse()
.some((item) => { .some((item) => {
if (item.children?.length) { if (item.children?.length) {
foundItem = getPrevItem(item.children, item.children.length) const childItem = getPrevItem(item.children, item.children.length)
if (childItem) {
foundItem = {
...childItem,
parent: item,
}
}
} else if (item.path) { } else if (item.path) {
foundItem = item foundItem = item
} }
@@ -87,13 +98,19 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
const getNextItem = ( const getNextItem = (
items: SidebarItemType[], items: SidebarItemType[],
index: number index: number
): SidebarItemType | undefined => { ): SidebarItemWithParent | undefined => {
let foundItem: SidebarItemType | undefined let foundItem: SidebarItemWithParent | undefined
items.slice(index + 1).some((item) => { items.slice(index + 1).some((item) => {
if (item.path) { if (item.path) {
foundItem = item foundItem = item
} else if (item.children?.length) { } else if (item.children?.length) {
foundItem = getNextItem(item.children, -1) const childItem = getNextItem(item.children, -1)
if (childItem) {
foundItem = {
...childItem,
parent: item,
}
}
} }
return foundItem !== undefined return foundItem !== undefined
@@ -157,6 +174,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
? { ? {
title: result.prevItem.title, title: result.prevItem.title,
link: result.prevItem.path || "", link: result.prevItem.path || "",
parentTitle: result.prevItem.parent?.title,
} }
: undefined : undefined
) )
@@ -165,6 +183,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
? { ? {
title: result.nextItem.title, title: result.nextItem.title,
link: result.nextItem.path || "", link: result.nextItem.path || "",
parentTitle: result.nextItem.parent?.title,
} }
: undefined : undefined
) )
@@ -59,7 +59,7 @@ export type SidebarContextType = {
setDesktopSidebarOpen: React.Dispatch<React.SetStateAction<boolean>> setDesktopSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>
staticSidebarItems?: boolean staticSidebarItems?: boolean
shouldHandleHashChange: boolean shouldHandleHashChange: boolean
sidebarRef: React.RefObject<HTMLUListElement> sidebarRef: React.RefObject<HTMLDivElement>
goBack: () => void goBack: () => void
} & SidebarStyleOptions } & SidebarStyleOptions
@@ -198,7 +198,7 @@ export const SidebarProvider = ({
const [activePath, setActivePath] = useState<string | null>("") const [activePath, setActivePath] = useState<string | null>("")
const [mobileSidebarOpen, setMobileSidebarOpen] = useState<boolean>(false) const [mobileSidebarOpen, setMobileSidebarOpen] = useState<boolean>(false)
const [desktopSidebarOpen, setDesktopSidebarOpen] = useState(true) const [desktopSidebarOpen, setDesktopSidebarOpen] = useState(true)
const sidebarRef = useRef<HTMLUListElement>(null) const sidebarRef = useRef<HTMLDivElement>(null)
const pathname = usePathname() const pathname = usePathname()
const router = useRouter() const router = useRouter()
@@ -311,7 +311,7 @@ export const SidebarProvider = ({
} }
setActivePath(backItem.path!) setActivePath(backItem.path!)
setCurrentItems(currentItems.previousSidebar) setCurrentItems(previousSidebar)
router.replace(backItem.path!) router.replace(backItem.path!)
} }
@@ -405,7 +405,10 @@ export const SidebarProvider = ({
bottom: [], bottom: [],
mobile: items.mobile, mobile: items.mobile,
parentItem: parentItem, parentItem: parentItem,
previousSidebar: currentItems, previousSidebar:
currentItems?.previousSidebar?.parentItem?.path !== parentItem.path
? currentItems
: undefined,
}) })
} }
}, [getCurrentSidebar, activePath]) }, [getCurrentSidebar, activePath])
@@ -1,17 +1,29 @@
import { NavbarLinkProps, navbarItems } from ".." import { NavbarItem, legacyNavbarItems, navbarItemsV1, navbarItemsV2 } from ".."
type Options = { type Options = {
basePath: string basePath: string
activePath: string activePath: string
version?: "v1" | "v2" | "legacy"
} }
export function getNavbarItems({ export function getNavbarItems({
basePath, basePath,
activePath, activePath,
}: Options): NavbarLinkProps[] { version = "legacy"
return navbarItems.map((item) => ({ }: Options): NavbarItem[] {
...item, const navbarItems = version === "v2" ? navbarItemsV2 : version === "v1" ? navbarItemsV1 : legacyNavbarItems
isActive: activePath === item.href, return navbarItems.map((item) => {
href: `${basePath}${item.href}`, if (item.type === "divider") {
})) return item
}
return {
...item,
props: {
...item.props,
isActive: activePath === item.props?.href,
href: `${basePath}${item.props?.href}`,
}
}
})
} }
+1
View File
@@ -3,6 +3,7 @@ import { SidebarSectionItemsType } from "./sidebar.js"
export declare type DocsConfig = { export declare type DocsConfig = {
titleSuffix?: string titleSuffix?: string
baseUrl: string baseUrl: string
basePath?: string
sidebar: SidebarSectionItemsType sidebar: SidebarSectionItemsType
filesBasePath?: string filesBasePath?: string
useNextLinks?: boolean useNextLinks?: boolean