mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
feat(docs): add clickable mdx heading anchors
This commit is contained in:
@@ -55,69 +55,156 @@ function ComponentsListWrapper() {
|
||||
)
|
||||
}
|
||||
|
||||
function getNodeText(node: React.ReactNode): string {
|
||||
if (typeof node === "string" || typeof node === "number") {
|
||||
return String(node)
|
||||
}
|
||||
|
||||
if (Array.isArray(node)) {
|
||||
return node.map((child) => getNodeText(child)).join("")
|
||||
}
|
||||
|
||||
if (React.isValidElement<{ children?: React.ReactNode }>(node)) {
|
||||
return getNodeText(node.props.children)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
function getHeadingId(children: React.ReactNode) {
|
||||
const id = getNodeText(children)
|
||||
.trim()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/'/g, "")
|
||||
.replace(/\?/g, "")
|
||||
.toLowerCase()
|
||||
|
||||
return id || undefined
|
||||
}
|
||||
|
||||
function HeadingAnchor({
|
||||
id,
|
||||
children,
|
||||
}: {
|
||||
id?: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
if (!id) {
|
||||
return children
|
||||
}
|
||||
|
||||
return (
|
||||
<a className="group no-underline" href={`#${id}`}>
|
||||
<span className="underline-offset-4 group-hover:underline">
|
||||
{children}
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="ml-2 text-muted-foreground opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
#
|
||||
</span>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export const mdxComponents = {
|
||||
h1: ({ className, ...props }: React.ComponentProps<"h1">) => (
|
||||
<h1
|
||||
className={cn(
|
||||
"mt-2 scroll-m-28 font-heading text-3xl font-bold tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h2: ({ className, ...props }: React.ComponentProps<"h2">) => {
|
||||
h1: ({ className, children, id, ...props }: React.ComponentProps<"h1">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h1
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"mt-2 scroll-m-28 font-heading text-3xl font-bold tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h1>
|
||||
)
|
||||
},
|
||||
h2: ({ className, children, id, ...props }: React.ComponentProps<"h2">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h2
|
||||
id={props.children
|
||||
?.toString()
|
||||
.replace(/ /g, "-")
|
||||
.replace(/'/g, "")
|
||||
.replace(/\?/g, "")
|
||||
.toLowerCase()}
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"[&+]*:[code]:text-xl mt-10 scroll-m-28 font-heading text-xl font-medium tracking-tight first:mt-0 lg:mt-12 [&+.steps]:mt-0! [&+.steps>h3]:mt-4! [&+h3]:mt-6! [&+p]:mt-4!",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h2>
|
||||
)
|
||||
},
|
||||
h3: ({ className, children, id, ...props }: React.ComponentProps<"h3">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h3
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"mt-12 scroll-m-28 font-heading text-lg font-medium tracking-tight [&+p]:mt-4! *:[code]:text-xl",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h3>
|
||||
)
|
||||
},
|
||||
h4: ({ className, children, id, ...props }: React.ComponentProps<"h4">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h4
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 font-heading text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h4>
|
||||
)
|
||||
},
|
||||
h5: ({ className, children, id, ...props }: React.ComponentProps<"h5">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h5
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h5>
|
||||
)
|
||||
},
|
||||
h6: ({ className, children, id, ...props }: React.ComponentProps<"h6">) => {
|
||||
const headingId = id ?? getHeadingId(children)
|
||||
|
||||
return (
|
||||
<h6
|
||||
id={headingId}
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<HeadingAnchor id={headingId}>{children}</HeadingAnchor>
|
||||
</h6>
|
||||
)
|
||||
},
|
||||
h3: ({ className, ...props }: React.ComponentProps<"h3">) => (
|
||||
<h3
|
||||
className={cn(
|
||||
"mt-12 scroll-m-28 font-heading text-lg font-medium tracking-tight [&+p]:mt-4! *:[code]:text-xl",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h4: ({ className, ...props }: React.ComponentProps<"h4">) => (
|
||||
<h4
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 font-heading text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h5: ({ className, ...props }: React.ComponentProps<"h5">) => (
|
||||
<h5
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h6: ({ className, ...props }: React.ComponentProps<"h6">) => (
|
||||
<h6
|
||||
className={cn(
|
||||
"mt-8 scroll-m-28 text-base font-medium tracking-tight",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
a: ({ className, ...props }: React.ComponentProps<"a">) => (
|
||||
<a
|
||||
className={cn("font-medium underline underline-offset-4", className)}
|
||||
|
||||
Reference in New Issue
Block a user