diff --git a/apps/web/app/(marketing)/_components/site-navigation.tsx b/apps/web/app/(marketing)/_components/site-navigation.tsx index 038436a94..567d0dceb 100644 --- a/apps/web/app/(marketing)/_components/site-navigation.tsx +++ b/apps/web/app/(marketing)/_components/site-navigation.tsx @@ -48,12 +48,8 @@ export function SiteNavigation() { return ( <>
- - + + {NavItems} diff --git a/apps/web/app/(marketing)/blog/_components/html-renderer.module.css b/apps/web/app/(marketing)/blog/_components/html-renderer.module.css index 605860041..1c05748a4 100644 --- a/apps/web/app/(marketing)/blog/_components/html-renderer.module.css +++ b/apps/web/app/(marketing)/blog/_components/html-renderer.module.css @@ -1,25 +1,29 @@ +.HTML { + @apply text-secondary-foreground; +} + .HTML h1 { - @apply mt-14 text-4xl font-bold font-heading; + @apply mt-14 text-4xl font-semibold font-heading tracking-tight ; } .HTML h2 { - @apply mb-4 mt-12 text-2xl font-semibold lg:text-3xl font-heading; + @apply mb-4 mt-12 font-semibold text-2xl font-heading tracking-tight; } .HTML h3 { - @apply mt-10 text-2xl font-bold font-heading; + @apply mt-10 text-xl font-semibold font-heading tracking-tight; } .HTML h4 { - @apply mt-8 text-xl font-bold; + @apply mt-8 text-lg font-medium tracking-tight; } .HTML h5 { - @apply mt-6 text-lg font-semibold; + @apply mt-6 text-base font-medium tracking-tight; } .HTML h6 { - @apply mt-2 text-base font-medium; + @apply mt-2 text-sm font-normal tracking-tight; } /** @@ -37,11 +41,11 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm } .HTML p { - @apply mb-4 mt-2 text-base leading-7 text-muted-foreground; + @apply mb-4 mt-2 text-base leading-7; } .HTML li { - @apply relative my-1.5 text-base leading-7 text-muted-foreground; + @apply relative my-1.5 text-base leading-7; } .HTML ul > li:before { @@ -93,5 +97,5 @@ For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomm } .HTML pre { - @apply my-6 text-sm text-current border p-6 rounded-lg; + @apply my-6 text-sm text-current border p-6 rounded-lg overflow-x-scroll; } diff --git a/apps/web/app/(marketing)/docs/[...slug]/page.tsx b/apps/web/app/(marketing)/docs/[...slug]/page.tsx index 5bfed102a..c56042ea5 100644 --- a/apps/web/app/(marketing)/docs/[...slug]/page.tsx +++ b/apps/web/app/(marketing)/docs/[...slug]/page.tsx @@ -5,12 +5,17 @@ import { notFound } from 'next/navigation'; import { ContentRenderer, createCmsClient } from '@kit/cms'; import { If } from '@kit/ui/if'; import { Separator } from '@kit/ui/separator'; +import { cn } from '@kit/ui/utils'; import { withI18n } from '~/lib/i18n/with-i18n'; -import { SitePageHeader } from '../../_components/site-page-header'; +// styles import styles from '../../blog/_components/html-renderer.module.css'; + +// local imports import { DocsCards } from '../_components/docs-cards'; +import { DocsTableOfContents } from '../_components/docs-table-of-contents'; +import { extractHeadingsFromJSX } from '../_lib/utils'; const getPageBySlug = cache(pageLoader); @@ -50,26 +55,35 @@ async function DocumentationPage({ params }: DocumentationPageProps) { const description = page?.description ?? ''; - return ( -
- + const headings = extractHeadingsFromJSX( + page.content as { + props: { children: React.ReactElement[] }; + }, + ); + + return ( +
+
+
+
+

{page.title}

+ +

+ {description} +

+
-
-
- 0}> - - - - +
+ + 0}> + + + +
); } diff --git a/apps/web/app/(marketing)/docs/_components/docs-nav-link.tsx b/apps/web/app/(marketing)/docs/_components/docs-nav-link.tsx new file mode 100644 index 000000000..960ce90b9 --- /dev/null +++ b/apps/web/app/(marketing)/docs/_components/docs-nav-link.tsx @@ -0,0 +1,26 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +import { SidebarMenuButton, SidebarMenuItem } from '@kit/ui/shadcn-sidebar'; +import { cn, isRouteActive } from '@kit/ui/utils'; + +export function DocsNavLink({ label, url }: { label: string; url: string }) { + const currentPath = usePathname(); + const isCurrent = isRouteActive(url, currentPath, true); + + return ( + + + + {label} + + + + ); +} diff --git a/apps/web/app/(marketing)/docs/_components/docs-navigation.tsx b/apps/web/app/(marketing)/docs/_components/docs-navigation.tsx index 6fe22b971..1d21e7fb7 100644 --- a/apps/web/app/(marketing)/docs/_components/docs-navigation.tsx +++ b/apps/web/app/(marketing)/docs/_components/docs-navigation.tsx @@ -1,192 +1,68 @@ -'use client'; - -import { useEffect, useMemo, useState } from 'react'; - -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; - -import { Menu } from 'lucide-react'; - import { Cms } from '@kit/cms'; -import { isBrowser } from '@kit/shared/utils'; -import { Button } from '@kit/ui/button'; -import { If } from '@kit/ui/if'; -import { cn, isRouteActive } from '@kit/ui/utils'; +import { + Sidebar, + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuSub, +} from '@kit/ui/shadcn-sidebar'; -function DocsNavLink({ - label, - url, - level, - activePath, -}: { - label: string; - url: string; - level: number; - activePath: string; -}) { - const isCurrent = isRouteActive(url, activePath, true); - const isFirstLevel = level === 0; +import { DocsNavLink } from '~/(marketing)/docs/_components/docs-nav-link'; - return ( - - ); -} +import { FloatingDocumentationNavigation } from './floating-docs-navigation'; -function Node({ - node, - level, - activePath, -}: { - node: Cms.ContentItem; - level: number; - activePath: string; -}) { +function Node({ node, level }: { node: Cms.ContentItem; level: number }) { const pathPrefix = `/docs`; const url = `${pathPrefix}/${node.slug}`; return ( <> - + {(node.children ?? []).length > 0 && ( - + )} ); } -function Tree({ - pages, - level, - activePath, -}: { - pages: Cms.ContentItem[]; - level: number; - activePath: string; -}) { +function Tree({ pages, level }: { pages: Cms.ContentItem[]; level: number }) { + if (level === 0) { + return pages.map((treeNode, index) => ( + + + + + + + + )); + } + return ( -
0, - })} - > + {pages.map((treeNode, index) => ( - + ))} -
+ ); } export function DocsNavigation({ pages }: { pages: Cms.ContentItem[] }) { - const activePath = usePathname(); - return ( <> - + +
- + + +
); } - -function FloatingDocumentationNavigation({ - pages, - activePath, -}: React.PropsWithChildren<{ - pages: Cms.ContentItem[]; - activePath: string; -}>) { - const body = useMemo(() => { - return isBrowser() ? document.body : null; - }, []); - - const [isVisible, setIsVisible] = useState(false); - - const enableScrolling = (element: HTMLElement) => - (element.style.overflowY = ''); - - const disableScrolling = (element: HTMLElement) => - (element.style.overflowY = 'hidden'); - - // enable/disable body scrolling when the docs are toggled - useEffect(() => { - if (!body) { - return; - } - - if (isVisible) { - disableScrolling(body); - } else { - enableScrolling(body); - } - }, [isVisible, body]); - - // hide docs when navigating to another page - useEffect(() => { - setIsVisible(false); - }, [activePath]); - - const onClick = () => { - setIsVisible(!isVisible); - }; - - return ( - <> - -
- -
-
- - - - ); -} diff --git a/apps/web/app/(marketing)/docs/_components/docs-table-of-contents.tsx b/apps/web/app/(marketing)/docs/_components/docs-table-of-contents.tsx new file mode 100644 index 000000000..e06b4d026 --- /dev/null +++ b/apps/web/app/(marketing)/docs/_components/docs-table-of-contents.tsx @@ -0,0 +1,51 @@ +'use client'; + +import Link from 'next/link'; + +interface NavItem { + text: string; + level: number; + href: string; + children: NavItem[]; +} + +export function DocsTableOfContents(props: { data: NavItem[] }) { + const navData = props.data; + + return ( +
+
    + {navData.map((item) => ( +
  1. + + {item.text} + + {item.children && ( +
      + {item.children.map((child) => ( +
    1. + + {child.text} + +
    2. + ))} +
    + )} +
  2. + ))} +
+
+ ); +} diff --git a/apps/web/app/(marketing)/docs/_components/floating-docs-navigation.tsx b/apps/web/app/(marketing)/docs/_components/floating-docs-navigation.tsx new file mode 100644 index 000000000..840d8947a --- /dev/null +++ b/apps/web/app/(marketing)/docs/_components/floating-docs-navigation.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { useEffect, useMemo, useState } from 'react'; + +import { usePathname } from 'next/navigation'; + +import { Menu } from 'lucide-react'; + +import { isBrowser } from '@kit/shared/utils'; +import { Button } from '@kit/ui/button'; +import { If } from '@kit/ui/if'; + +export function FloatingDocumentationNavigation( + props: React.PropsWithChildren, +) { + const activePath = usePathname(); + + const body = useMemo(() => { + return isBrowser() ? document.body : null; + }, []); + + const [isVisible, setIsVisible] = useState(false); + + const enableScrolling = (element: HTMLElement) => + (element.style.overflowY = ''); + + const disableScrolling = (element: HTMLElement) => + (element.style.overflowY = 'hidden'); + + // enable/disable body scrolling when the docs are toggled + useEffect(() => { + if (!body) { + return; + } + + if (isVisible) { + disableScrolling(body); + } else { + enableScrolling(body); + } + }, [isVisible, body]); + + // hide docs when navigating to another page + useEffect(() => { + setIsVisible(false); + }, [activePath]); + + const onClick = () => { + setIsVisible(!isVisible); + }; + + return ( + <> + +
+ {props.children} +
+
+ + + + ); +} diff --git a/apps/web/app/(marketing)/docs/_lib/utils.ts b/apps/web/app/(marketing)/docs/_lib/utils.ts new file mode 100644 index 000000000..e714ec047 --- /dev/null +++ b/apps/web/app/(marketing)/docs/_lib/utils.ts @@ -0,0 +1,136 @@ +import { Cms } from '@kit/cms'; + +interface HeadingNode { + text: string; + level: number; + href: string; + children: HeadingNode[]; +} + +/** + * @name buildDocumentationTree + * @description Build a tree structure for the documentation pages. + * @param pages + */ +export function buildDocumentationTree(pages: Cms.ContentItem[]) { + const tree: Cms.ContentItem[] = []; + + pages.forEach((page) => { + if (page.parentId) { + const parent = pages.find((item) => item.slug === page.parentId); + + if (!parent) { + return; + } + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(page); + + // sort children by order + parent.children.sort((a, b) => a.order - b.order); + } else { + tree.push(page); + } + }); + + return tree.sort((a, b) => a.order - b.order); +} + +/** + * @name extractHeadingsFromJSX + * @description Extract headings from JSX. This is used to generate the table of contents for the documentation pages. + * @param jsx + */ +export function extractHeadingsFromJSX(jsx: { + props: { children: React.ReactElement[] }; +}) { + const headings: HeadingNode[] = []; + let currentH2: HeadingNode | null = null; + + function getTextContent( + children: React.ReactElement[] | string | React.ReactElement, + ): string { + if (typeof children === 'string') { + return children; + } + + if (Array.isArray(children)) { + return children.map((child) => getTextContent(child)).join(''); + } + + if ( + ( + children.props as { + children: React.ReactElement; + } + ).children + ) { + return getTextContent((children.props as { children: React.ReactElement }).children); + } + + return ''; + } + + jsx.props.children.forEach((node) => { + if (!node || typeof node !== 'object' || !('type' in node)) { + return; + } + + const nodeType = node.type as string; + + const text = getTextContent( + ( + node.props as { + children: React.ReactElement[]; + } + ).children, + ); + + if (nodeType === 'h1') { + const slug = generateSlug(text); + + headings.push({ + text, + level: 1, + href: `#${slug}`, + children: [], + }); + } else if (nodeType === 'h2') { + const slug = generateSlug(text); + + currentH2 = { + text, + level: 2, + href: `#${slug}`, + children: [], + }; + + if (headings.length > 0) { + headings[headings.length - 1]!.children.push(currentH2); + } else { + headings.push(currentH2); + } + } else if (nodeType === 'h3' && currentH2) { + const slug = generateSlug(text); + + currentH2.children.push({ + text, + level: 3, + href: `#${slug}`, + children: [], + }); + } + }); + + return headings; +} + +function generateSlug(text: string): string { + return text + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)/g, ''); +} diff --git a/apps/web/app/(marketing)/docs/layout.tsx b/apps/web/app/(marketing)/docs/layout.tsx index c4337487d..fee293755 100644 --- a/apps/web/app/(marketing)/docs/layout.tsx +++ b/apps/web/app/(marketing)/docs/layout.tsx @@ -1,56 +1,24 @@ -import { Cms } from '@kit/cms'; +import { SidebarProvider } from '@kit/ui/shadcn-sidebar'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; // local imports import { DocsNavigation } from './_components/docs-navigation'; import { getDocs } from './_lib/server/docs.loader'; +import { buildDocumentationTree } from './_lib/utils'; async function DocsLayout({ children }: React.PropsWithChildren) { const { resolvedLanguage } = await createI18nServerInstance(); - const pages = await getDocs(resolvedLanguage); + const docs = await getDocs(resolvedLanguage); + const tree = buildDocumentationTree(docs); return ( -
- + + {children} -
+ ); } export default DocsLayout; - -// we want to place all the children under their parent -// based on the property parentId -function buildDocumentationTree(pages: Cms.ContentItem[]) { - const tree: Cms.ContentItem[] = []; - const map: Record = {}; - - pages.forEach((page) => { - map[page.id] = page; - }); - - pages.forEach((page) => { - if (page.parentId) { - const parent = map[page.parentId]; - - if (!parent) { - return; - } - - if (!parent.children) { - parent.children = []; - } - - parent.children.push(page); - - // sort children by order - parent.children.sort((a, b) => a.order - b.order); - } else { - tree.push(page); - } - }); - - return tree.sort((a, b) => a.order - b.order); -} diff --git a/apps/web/app/(marketing)/docs/loading.tsx b/apps/web/app/(marketing)/docs/loading.tsx new file mode 100644 index 000000000..902f1f609 --- /dev/null +++ b/apps/web/app/(marketing)/docs/loading.tsx @@ -0,0 +1,3 @@ +import { GlobalLoader } from '@kit/ui/global-loader'; + +export default GlobalLoader; \ No newline at end of file diff --git a/apps/web/content/documentation/authentication/configuration.mdoc b/apps/web/content/documentation/authentication/configuration.mdoc index 08250384c..da7ffdc85 100644 --- a/apps/web/content/documentation/authentication/configuration.mdoc +++ b/apps/web/content/documentation/authentication/configuration.mdoc @@ -2,8 +2,7 @@ title: "Configuration" description: "Learn how authentication works in MakerKit and how to configure it." publishedAt: 2024-04-11 -order: 0 -parent: "authentication/authentication" +order: 1 status: "published" --- diff --git a/apps/web/content/documentation/getting-started/installing-dependencies.mdoc b/apps/web/content/documentation/getting-started/installing-dependencies.mdoc index 1af89bf83..ceeb75974 100644 --- a/apps/web/content/documentation/getting-started/installing-dependencies.mdoc +++ b/apps/web/content/documentation/getting-started/installing-dependencies.mdoc @@ -2,7 +2,6 @@ title: "Installing Dependencies" description: "Learn how to install dependencies for your project." publishedAt: 2024-04-11 -parent: "getting-started/getting-started" order: 0 status: "published" --- diff --git a/packages/cms/keystatic/src/content-renderer.tsx b/packages/cms/keystatic/src/content-renderer.tsx index 0fceb95af..e5d80dec4 100644 --- a/packages/cms/keystatic/src/content-renderer.tsx +++ b/packages/cms/keystatic/src/content-renderer.tsx @@ -1,3 +1,3 @@ export function KeystaticContentRenderer(props: { content: unknown }) { - return
{props.content as React.ReactNode}
; + return props.content as React.ReactNode; } diff --git a/packages/cms/keystatic/src/keystatic-client.ts b/packages/cms/keystatic/src/keystatic-client.ts index 27bb399e3..9ebcefba6 100644 --- a/packages/cms/keystatic/src/keystatic-client.ts +++ b/packages/cms/keystatic/src/keystatic-client.ts @@ -1,13 +1,8 @@ -import React from 'react'; - import { Cms, CmsClient } from '@kit/cms-types'; import { createKeystaticReader } from './create-reader'; -import { - CustomMarkdocComponents, - CustomMarkdocTags, -} from './custom-components'; import { PostEntryProps } from './keystatic.config'; +import { renderMarkdoc } from './markdoc'; export function createKeystaticClient() { return new KeystaticClient(); @@ -85,12 +80,78 @@ class KeystaticClient implements CmsClient { return right - left; }); - const items = await Promise.all( - filtered.slice(startOffset, endOffset).map(async (item) => { - const children = docs.filter((item) => item.entry.parent === item.slug); + function processItems(items: typeof docs) { + const result: typeof docs = [...items]; - return this.mapPost(item, children); - }), + const indexFiles = items.filter((item) => { + const parts = item.slug.split('/'); + + return ( + parts.length > 1 && + parts[parts.length - 1] === parts[parts.length - 2] + ); + }); + + function findParentIndex(pathParts: string[]): string | null { + // Try each level up from the current path until we find an index file + for (let i = pathParts.length - 1; i > 0; i--) { + const currentPath = pathParts.slice(0, i).join('/'); + + const possibleParent = indexFiles.find((indexFile) => { + const indexParts = indexFile.slug.split('/'); + const indexFolderPath = indexParts.slice(0, -1).join('/'); + + return indexFolderPath === currentPath; + }); + + if (possibleParent) { + return possibleParent.slug; + } + } + return null; + } + + result.forEach((item) => { + const pathParts = item.slug.split('/'); + + // Skip if this is a root level index file (e.g., "authentication/authentication") + if (pathParts.length === 2 && pathParts[0] === pathParts[1]) { + item.entry.parent = null; + return; + } + + // Check if current item is an index file + const isIndexFile = + pathParts[pathParts.length - 1] === pathParts[pathParts.length - 2]; + + if (isIndexFile) { + // For index files, find parent in the level above + const parentPath = pathParts.slice(0, -2); + if (parentPath.length > 0) { + item.entry.parent = findParentIndex( + parentPath.concat(parentPath[parentPath.length - 1]!), + ); + } else { + item.entry.parent = null; + } + } else { + // For regular files, find parent in the current folder + item.entry.parent = findParentIndex(pathParts); + } + }); + + return result; + } + + const itemsWithParents = processItems(filtered); + + const items = await Promise.all( + itemsWithParents + .slice(startOffset, endOffset) + .sort((a, b) => { + return (a.entry.order ?? 0) - (b.entry.order ?? 0); + }) + .map((item) => this.mapPost(item)), ); return { @@ -157,25 +218,17 @@ class KeystaticClient implements CmsClient { slug: string; }, >(item: Type, children: Type[] = []): Promise { - const { transform, renderers } = await import('@markdoc/markdoc'); - const publishedAt = item.entry.publishedAt ? new Date(item.entry.publishedAt) : new Date(); - const markdoc = await item.entry.content(); - - const content = transform(markdoc.node, { - tags: CustomMarkdocTags, - }); - - const html = renderers.react(content, React, { - components: CustomMarkdocComponents, - }); + const content = await item.entry.content(); + const html = await renderMarkdoc(content.node); return { id: item.slug, title: item.entry.title, + label: item.entry.label, url: item.slug, slug: item.slug, description: item.entry.description, @@ -201,7 +254,7 @@ class KeystaticClient implements CmsClient { parentId: item.entry.parent ?? undefined, order: item.entry.order ?? 1, children: await Promise.all( - children.map(async (child) => this.mapPost(child, [])), + children.map((child) => this.mapPost(child, [])), ), }; } diff --git a/packages/cms/keystatic/src/keystatic.config.ts b/packages/cms/keystatic/src/keystatic.config.ts index 4a570c00c..d33437769 100644 --- a/packages/cms/keystatic/src/keystatic.config.ts +++ b/packages/cms/keystatic/src/keystatic.config.ts @@ -66,6 +66,7 @@ function getKeystaticCollections(path: string) { format: { contentField: 'content' }, schema: { title: fields.slug({ name: { label: 'Title' } }), + label: fields.text({ label: 'Label', validation: { isRequired: false } }), image: fields.image({ label: 'Image', directory: 'public/site/images', @@ -101,6 +102,7 @@ function getKeystaticCollections(path: string) { format: { contentField: 'content' }, schema: { title: fields.slug({ name: { label: 'Title' } }), + label: fields.text({ label: 'Label', validation: { isRequired: false } }), content: getContentField(), image: fields.image({ label: 'Image', diff --git a/packages/cms/keystatic/src/markdoc-nodes.ts b/packages/cms/keystatic/src/markdoc-nodes.ts new file mode 100644 index 000000000..59a89bc68 --- /dev/null +++ b/packages/cms/keystatic/src/markdoc-nodes.ts @@ -0,0 +1,42 @@ +// Or replace this with your own function +import { Config, Node, RenderableTreeNode, Tag } from '@markdoc/markdoc'; + +function generateID( + children: Array, + attributes: Record, +) { + if (attributes.id && typeof attributes.id === 'string') { + return attributes.id; + } + + return children + .filter((child) => typeof child === 'string') + .join(' ') + .replace(/[?]/g, '') + .replace(/\s+/g, '-') + .toLowerCase(); +} + +const heading = { + children: ['inline'], + attributes: { + id: { type: String }, + level: { type: Number, required: true, default: 1 }, + }, + transform(node: Node, config: Config) { + const attributes = node.transformAttributes(config); + const children = node.transformChildren(config); + + const id = generateID(children, attributes); + + return new Tag( + `h${node.attributes.level}`, + { ...attributes, id }, + children, + ); + }, +}; + +export const MarkdocNodes = { + heading, +}; diff --git a/packages/cms/keystatic/src/markdoc.tsx b/packages/cms/keystatic/src/markdoc.tsx new file mode 100644 index 000000000..b4ca159f8 --- /dev/null +++ b/packages/cms/keystatic/src/markdoc.tsx @@ -0,0 +1,30 @@ +import React from 'react'; + +import { Node } from '@markdoc/markdoc'; + +import { + CustomMarkdocComponents, + CustomMarkdocTags, +} from './custom-components'; +import { MarkdocNodes } from './markdoc-nodes'; + +/** + * @name renderMarkdoc + * @description Renders a Markdoc tree to React + */ +export async function renderMarkdoc(node: Node) { + const { transform, renderers } = await import('@markdoc/markdoc'); + + const content = transform(node, { + tags: { + ...CustomMarkdocTags, + }, + nodes: { + ...MarkdocNodes, + }, + }); + + return renderers.react(content, React, { + components: CustomMarkdocComponents, + }); +} diff --git a/packages/cms/types/src/cms-client.ts b/packages/cms/types/src/cms-client.ts index de7fc8f09..bc4a90af9 100644 --- a/packages/cms/types/src/cms-client.ts +++ b/packages/cms/types/src/cms-client.ts @@ -3,6 +3,7 @@ export namespace Cms { export interface ContentItem { id: string; title: string; + label: string | undefined; url: string; description: string | undefined; content: unknown; diff --git a/packages/cms/wordpress/src/wp-client.ts b/packages/cms/wordpress/src/wp-client.ts index 60a7d191e..1b1450884 100644 --- a/packages/cms/wordpress/src/wp-client.ts +++ b/packages/cms/wordpress/src/wp-client.ts @@ -139,6 +139,7 @@ class WordpressClient implements CmsClient { return { id: item.id.toString(), title: item.title.rendered, + label: item.title.rendered, content: item.content.rendered, description: item.excerpt.rendered, image, @@ -217,6 +218,7 @@ class WordpressClient implements CmsClient { description: item.excerpt.rendered, children: [], title: item.title.rendered, + label: item.title.rendered, content: item.content.rendered, slug: item.slug, publishedAt: item.date, diff --git a/packages/ui/src/shadcn/sidebar.tsx b/packages/ui/src/shadcn/sidebar.tsx index cae9431ea..5b8ddde81 100644 --- a/packages/ui/src/shadcn/sidebar.tsx +++ b/packages/ui/src/shadcn/sidebar.tsx @@ -209,7 +209,7 @@ const Sidebar = React.forwardRef< HTMLDivElement, React.ComponentProps<'div'> & { side?: 'left' | 'right'; - variant?: 'sidebar' | 'floating' | 'inset'; + variant?: 'sidebar' | 'floating' | 'inset' | 'ghost'; collapsible?: 'offcanvas' | 'icon' | 'none'; } >( @@ -285,7 +285,13 @@ const Sidebar = React.forwardRef< button]:hidden', + { + 'bg-background': variant === 'ghost', + 'bg-sidebar': variant !== 'ghost', + }, + )} style={ { '--sidebar-width': SIDEBAR_WIDTH_MOBILE, @@ -313,12 +319,15 @@ const Sidebar = React.forwardRef< {/* This is what handles the sidebar gap on desktop */}
{children}
@@ -862,11 +876,7 @@ export function SidebarNavigation({ const ContentContainer = (props: React.PropsWithChildren) => { if (item.collapsible) { - return ( - - {props.children} - - ); + return {props.children}; } return props.children;