Skip to content

Commit e692ea8

Browse files
committed
feat: toc for doc page
1 parent 749f7a2 commit e692ea8

File tree

3 files changed

+51
-42
lines changed

3 files changed

+51
-42
lines changed

src/components/mdx/mdx-components.tsx

+37-42
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { ComponentProps } from "react";
2+
import React from "react";
23
import NavLink from "next/link";
34
import { Code } from "bright";
45
import { CodeTabs } from "@/components/code-highlighter/code-tabs";
@@ -10,6 +11,7 @@ import {
1011
import { ComponentSource } from "@/components/component-source";
1112
import { DocsList, type DocsListProps } from "@/components/docs/docs-list";
1213
import { IconsExplorer } from "@/components/icons-explorer";
14+
import { slugify } from "@/utils/string";
1315
import { cn } from "@/lib/utils/classes";
1416

1517
export const Link = ({
@@ -42,52 +44,45 @@ export const Link = ({
4244
);
4345
};
4446

47+
function createHeading(level: number, className?: string) {
48+
const Component = ({
49+
children,
50+
...props
51+
}: React.HTMLAttributes<HTMLHeadingElement>) => {
52+
const slug = slugify(children as string);
53+
return React.createElement(
54+
`h${level}`,
55+
{ id: slug, className, ...props },
56+
[
57+
React.createElement("a", {
58+
href: `#${slug}`,
59+
key: `link-${slug}`,
60+
}),
61+
],
62+
children
63+
);
64+
};
65+
66+
Component.displayName = `Heading${level}`;
67+
return Component;
68+
}
69+
4570
export const components = {
46-
h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
47-
<h1
48-
className={cn("font-heading mt-2 scroll-m-20 text-4xl font-bold", className)}
49-
{...props}
50-
/>
51-
),
52-
h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
53-
<h2
54-
className={cn(
55-
"font-heading mt-12 scroll-m-20 border-b pb-2 text-xl font-semibold tracking-tight first:mt-0",
56-
className
57-
)}
58-
{...props}
59-
/>
60-
),
61-
h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
62-
<h3
63-
className={cn(
64-
"font-heading mt-8 scroll-m-20 text-xl font-semibold tracking-tight",
65-
className
66-
)}
67-
{...props}
68-
/>
69-
),
70-
h4: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
71-
<h4
72-
className={cn(
73-
"font-heading mt-8 scroll-m-20 text-lg font-semibold tracking-tight",
74-
className
75-
)}
76-
{...props}
77-
/>
71+
h1: createHeading(1, "font-heading mt-2 scroll-m-20 text-4xl font-bold"),
72+
h2: createHeading(
73+
2,
74+
"font-heading mt-12 scroll-m-20 border-b pb-2 text-xl font-semibold tracking-tight first:mt-0"
7875
),
79-
h5: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
80-
<h5
81-
className={cn("mt-8 scroll-m-20 text-lg font-semibold tracking-tight", className)}
82-
{...props}
83-
/>
76+
h3: createHeading(
77+
3,
78+
"font-heading mt-8 scroll-m-20 text-xl font-semibold tracking-tight"
8479
),
85-
h6: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
86-
<h6
87-
className={cn("mt-8 scroll-m-20 text-base font-semibold tracking-tight", className)}
88-
{...props}
89-
/>
80+
h4: createHeading(
81+
4,
82+
"font-heading mt-8 scroll-m-20 text-lg font-semibold tracking-tight"
9083
),
84+
h5: createHeading(5, "mt-8 scroll-m-20 text-lg font-semibold tracking-tight"),
85+
h6: createHeading(6, "mt-8 scroll-m-20 text-base font-semibold tracking-tight"),
9186
a: Link,
9287
p: ({ className, ...props }: ComponentProps<"p">) => (
9388
<p className={cn("leading-7 [&:not(:first-child)]:mt-4", className)} {...props} />

src/styles/globals.css

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
* {
5454
@apply border-border;
5555
}
56+
html {
57+
@apply scroll-smooth;
58+
}
5659
body {
5760
@apply bg-background text-foreground;
5861
/* white-space: pre-wrap; */

src/utils/string.ts

+11
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@ export function removeLastS(word: string): string {
44
}
55
return word;
66
}
7+
8+
export function slugify(str: string) {
9+
return str
10+
.toString()
11+
.toLowerCase()
12+
.trim() // Remove whitespace from both ends of a string
13+
.replace(/\s+/g, "-") // Replace spaces with -
14+
.replace(/&/g, "-and-") // Replace & with 'and'
15+
.replace(/[^\w\-]+/g, "") // Remove all non-word characters except for -
16+
.replace(/\-\-+/g, "-"); // Replace multiple - with single -
17+
}

0 commit comments

Comments
 (0)