Skip to content

[Portal] Add next button to inner page footers #7605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/portal/src/components/Document/AutoNextPageButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client";

import { usePathname } from "next/navigation";
import type { SidebarLink } from "@/components/others/Sidebar";
import { NextPageButton } from "./NextPageButton";
import { getNextPageFromSidebar } from "./utils/getNextPageFromSidebar";

export function AutoNextPageButton({
sidebarLinks,
}: {
sidebarLinks: SidebarLink[];
}) {
const pathname = usePathname();

// Don't show next button on home page
if (pathname === "/") {
return null;
}

const nextPage = getNextPageFromSidebar(sidebarLinks, pathname);

if (!nextPage) {
return null;
}

return <NextPageButton href={nextPage.href} name={nextPage.name} />;
}
16 changes: 16 additions & 0 deletions apps/portal/src/components/Document/NextPageButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ArrowRightIcon } from "lucide-react";
import Link from "next/link";

export function NextPageButton(props: { href: string; name: string }) {
return (
<Link
className="inline-flex items-center rounded-lg border text-sm duration-200 hover:border-active-border"
href={props.href}
>
<div className="border-r-2 p-2.5 font-semibold">Next: {props.name}</div>
<div className="p-2.5">
<ArrowRightIcon className="size-5" />
</div>
</Link>
);
}
14 changes: 12 additions & 2 deletions apps/portal/src/components/Document/PageFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@ import {
VideoIcon,
} from "lucide-react";
import { Feedback } from "../others/Feedback";
import type { SidebarLink } from "../others/Sidebar";
import { Subscribe } from "../others/Subscribe";
import { DocLink } from ".";
import { AutoEditPageButton } from "./AutoEditPageButton";
import { AutoNextPageButton } from "./AutoNextPageButton";

export function PageFooter(props: { editPageButton?: true }) {
export function PageFooter(props: {
editPageButton?: true;
sidebarLinks?: SidebarLink[];
}) {
return (
<footer className="flex flex-col gap-7 pb-20" data-noindex>
<div className="flex flex-col justify-between gap-7 md:flex-row md:items-center">
{props.editPageButton && <AutoEditPageButton />}
<div className="flex gap-4">
{props.editPageButton && <AutoEditPageButton />}
{props.sidebarLinks && (
<AutoNextPageButton sidebarLinks={props.sidebarLinks} />
)}
</div>
<Feedback />
</div>
<div className="h-1 border-t" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type { SidebarLink } from "@/components/others/Sidebar";

interface NextPageInfo {
href: string;
name: string;
}

// Helper function to flatten sidebar links into a linear array
function flattenSidebarLinks(
links: SidebarLink[],
): Array<{ href: string; name: string }> {
const result: Array<{ href: string; name: string }> = [];

for (const link of links) {
if ("separator" in link) {
// Skip separators
continue;
}

if ("links" in link) {
// Handle LinkGroup
if (link.href) {
result.push({ href: link.href, name: link.name });
}
// Recursively flatten nested links
result.push(...flattenSidebarLinks(link.links));
} else {
// Handle LinkMeta
result.push({ href: link.href, name: link.name });
}
}

return result;
}

// Helper function to check if two paths are the same
function isSamePage(pathname: string, pathOrHref: string): boolean {
try {
if (pathOrHref === pathname) {
return true;
}

// Handle relative URLs by creating full URLs
const currentUrl = new URL(pathname, "http://localhost");
const linkUrl = new URL(pathOrHref, "http://localhost");

return currentUrl.pathname === linkUrl.pathname;
} catch {
return false;
}
}

export function getNextPageFromSidebar(
sidebarLinks: SidebarLink[],
currentPathname: string,
): NextPageInfo | null {
// Flatten all sidebar links into a linear array
const flatLinks = flattenSidebarLinks(sidebarLinks);

// Find the current page index
const currentIndex = flatLinks.findIndex((link) =>
isSamePage(currentPathname, link.href),
);

// If current page is not found or is the last page, return null
if (currentIndex === -1 || currentIndex === flatLinks.length - 1) {
return null;
}

// Return the next page
const nextPage = flatLinks[currentIndex + 1];
if (!nextPage) {
return null;
}

return {
href: nextPage.href,
name: nextPage.name,
};
}
11 changes: 9 additions & 2 deletions apps/portal/src/components/Layouts/DocLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ type DocLayoutProps = {
export function DocLayout(props: DocLayoutProps) {
return (
<div
className={`container relative flex flex-col gap-6 xl:grid ${props.showTableOfContents !== false ? "xl:grid-cols-[280px_820px_1fr]" : "xl:grid-cols-[280px_1100px]"}`}
className={`container relative flex flex-col gap-6 xl:grid ${
props.showTableOfContents !== false
? "xl:grid-cols-[280px_820px_1fr]"
: "xl:grid-cols-[280px_1100px]"
}`}
style={{
minHeight: "calc(100vh - var(--sticky-top-height))",
}}
Expand All @@ -48,7 +52,10 @@ export function DocLayout(props: DocLayoutProps) {
>
<div className="grow xl:mt-6">{props.children}</div>
<div className="mt-16 xl:mt-20">
<PageFooter editPageButton={props.editPageButton} />
<PageFooter
editPageButton={props.editPageButton}
sidebarLinks={props.sideBar.links}
/>
</div>
</main>
{props.showTableOfContents !== false && <TableOfContentsSideBar />}
Expand Down
Loading