Skip to content

Commit a7544f7

Browse files
[Portal] Add next button to inner page footers (#7605)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent bd3d645 commit a7544f7

File tree

5 files changed

+144
-4
lines changed

5 files changed

+144
-4
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"use client";
2+
3+
import { usePathname } from "next/navigation";
4+
import type { SidebarLink } from "@/components/others/Sidebar";
5+
import { NextPageButton } from "./NextPageButton";
6+
import { getNextPageFromSidebar } from "./utils/getNextPageFromSidebar";
7+
8+
export function AutoNextPageButton({
9+
sidebarLinks,
10+
}: {
11+
sidebarLinks: SidebarLink[];
12+
}) {
13+
const pathname = usePathname();
14+
15+
// Don't show next button on home page
16+
if (pathname === "/") {
17+
return null;
18+
}
19+
20+
const nextPage = getNextPageFromSidebar(sidebarLinks, pathname);
21+
22+
if (!nextPage) {
23+
return null;
24+
}
25+
26+
return <NextPageButton href={nextPage.href} name={nextPage.name} />;
27+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ArrowRightIcon } from "lucide-react";
2+
import Link from "next/link";
3+
4+
export function NextPageButton(props: { href: string; name: string }) {
5+
return (
6+
<Link
7+
className="inline-flex items-center rounded-lg border text-sm duration-200 hover:border-active-border"
8+
href={props.href}
9+
>
10+
<div className="border-r-2 p-2.5 font-semibold">Next: {props.name}</div>
11+
<div className="p-2.5">
12+
<ArrowRightIcon className="size-5" />
13+
</div>
14+
</Link>
15+
);
16+
}

apps/portal/src/components/Document/PageFooter.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,25 @@ import {
66
VideoIcon,
77
} from "lucide-react";
88
import { Feedback } from "../others/Feedback";
9+
import type { SidebarLink } from "../others/Sidebar";
910
import { Subscribe } from "../others/Subscribe";
1011
import { DocLink } from ".";
1112
import { AutoEditPageButton } from "./AutoEditPageButton";
13+
import { AutoNextPageButton } from "./AutoNextPageButton";
1214

13-
export function PageFooter(props: { editPageButton?: true }) {
15+
export function PageFooter(props: {
16+
editPageButton?: true;
17+
sidebarLinks?: SidebarLink[];
18+
}) {
1419
return (
1520
<footer className="flex flex-col gap-7 pb-20" data-noindex>
1621
<div className="flex flex-col justify-between gap-7 md:flex-row md:items-center">
17-
{props.editPageButton && <AutoEditPageButton />}
22+
<div className="flex gap-4">
23+
{props.editPageButton && <AutoEditPageButton />}
24+
{props.sidebarLinks && (
25+
<AutoNextPageButton sidebarLinks={props.sidebarLinks} />
26+
)}
27+
</div>
1828
<Feedback />
1929
</div>
2030
<div className="h-1 border-t" />
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { SidebarLink } from "@/components/others/Sidebar";
2+
3+
interface NextPageInfo {
4+
href: string;
5+
name: string;
6+
}
7+
8+
// Helper function to flatten sidebar links into a linear array
9+
function flattenSidebarLinks(
10+
links: SidebarLink[],
11+
): Array<{ href: string; name: string }> {
12+
const result: Array<{ href: string; name: string }> = [];
13+
14+
for (const link of links) {
15+
if ("separator" in link) {
16+
// Skip separators
17+
continue;
18+
}
19+
20+
if ("links" in link) {
21+
// Handle LinkGroup
22+
if (link.href) {
23+
result.push({ href: link.href, name: link.name });
24+
}
25+
// Recursively flatten nested links
26+
result.push(...flattenSidebarLinks(link.links));
27+
} else {
28+
// Handle LinkMeta
29+
result.push({ href: link.href, name: link.name });
30+
}
31+
}
32+
33+
return result;
34+
}
35+
36+
// Helper function to check if two paths are the same
37+
function isSamePage(pathname: string, pathOrHref: string): boolean {
38+
try {
39+
if (pathOrHref === pathname) {
40+
return true;
41+
}
42+
43+
// Handle relative URLs by creating full URLs
44+
const currentUrl = new URL(pathname, "http://localhost");
45+
const linkUrl = new URL(pathOrHref, "http://localhost");
46+
47+
return currentUrl.pathname === linkUrl.pathname;
48+
} catch {
49+
return false;
50+
}
51+
}
52+
53+
export function getNextPageFromSidebar(
54+
sidebarLinks: SidebarLink[],
55+
currentPathname: string,
56+
): NextPageInfo | null {
57+
// Flatten all sidebar links into a linear array
58+
const flatLinks = flattenSidebarLinks(sidebarLinks);
59+
60+
// Find the current page index
61+
const currentIndex = flatLinks.findIndex((link) =>
62+
isSamePage(currentPathname, link.href),
63+
);
64+
65+
// If current page is not found or is the last page, return null
66+
if (currentIndex === -1 || currentIndex === flatLinks.length - 1) {
67+
return null;
68+
}
69+
70+
// Return the next page
71+
const nextPage = flatLinks[currentIndex + 1];
72+
if (!nextPage) {
73+
return null;
74+
}
75+
76+
return {
77+
href: nextPage.href,
78+
name: nextPage.name,
79+
};
80+
}

apps/portal/src/components/Layouts/DocLayout.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ type DocLayoutProps = {
2525
export function DocLayout(props: DocLayoutProps) {
2626
return (
2727
<div
28-
className={`container relative flex flex-col gap-6 xl:grid ${props.showTableOfContents !== false ? "xl:grid-cols-[280px_820px_1fr]" : "xl:grid-cols-[280px_1100px]"}`}
28+
className={`container relative flex flex-col gap-6 xl:grid ${
29+
props.showTableOfContents !== false
30+
? "xl:grid-cols-[280px_820px_1fr]"
31+
: "xl:grid-cols-[280px_1100px]"
32+
}`}
2933
style={{
3034
minHeight: "calc(100vh - var(--sticky-top-height))",
3135
}}
@@ -48,7 +52,10 @@ export function DocLayout(props: DocLayoutProps) {
4852
>
4953
<div className="grow xl:mt-6">{props.children}</div>
5054
<div className="mt-16 xl:mt-20">
51-
<PageFooter editPageButton={props.editPageButton} />
55+
<PageFooter
56+
editPageButton={props.editPageButton}
57+
sidebarLinks={props.sideBar.links}
58+
/>
5259
</div>
5360
</main>
5461
{props.showTableOfContents !== false && <TableOfContentsSideBar />}

0 commit comments

Comments
 (0)