135 lines
3.8 KiB
TypeScript
135 lines
3.8 KiB
TypeScript
import Link from "next/link";
|
|
import { DeveloperDocumentation } from "@/components/DeveloperDocumentation";
|
|
import { notFound } from "next/navigation";
|
|
import { Header, Footer } from "@/components/Shell";
|
|
import guides from "@/shared/player-docs.json";
|
|
import styles from "./page.module.css";
|
|
import { getI18n } from "@/lib/i18n";
|
|
|
|
export const metadata = { title: "Player docs" };
|
|
|
|
function inline(text: string) {
|
|
return text
|
|
.split(/(\*\*[^*]+\*\*|\[[^\]]+\]\(https:\/\/[^)]+\))/g)
|
|
.map((part, index) => {
|
|
if (part.startsWith("**"))
|
|
return <strong key={index}>{part.slice(2, -2)}</strong>;
|
|
const link = part.match(/^\[([^\]]+)\]\((https:\/\/[^)]+)\)$/);
|
|
return link ? (
|
|
<a key={index} href={link[2]}>
|
|
{link[1]}
|
|
</a>
|
|
) : (
|
|
part
|
|
);
|
|
});
|
|
}
|
|
|
|
function content(markdown: string) {
|
|
return markdown.split(/\n\s*\n/).map((block, index) => {
|
|
const heading = block.match(/^(#{1,3}) (.+)$/);
|
|
if (heading) {
|
|
const text = inline(heading[2]);
|
|
return heading[1].length === 1 ? (
|
|
<h1 key={index}>{text}</h1>
|
|
) : heading[1].length === 2 ? (
|
|
<h2 key={index}>{text}</h2>
|
|
) : (
|
|
<h3 key={index}>{text}</h3>
|
|
);
|
|
}
|
|
const image = block.match(/^!\[([^\]]*)\]\((https:\/\/[^)]+)\)$/);
|
|
if (image)
|
|
return (
|
|
<figure key={index}>
|
|
<img src={image[2]} alt={image[1]} loading="lazy" width="420" />
|
|
</figure>
|
|
);
|
|
if (block.startsWith("> "))
|
|
return (
|
|
<blockquote key={index}>
|
|
{inline(block.replace(/^> /gm, ""))}
|
|
</blockquote>
|
|
);
|
|
const lines = block.split("\n");
|
|
if (lines.every((line) => /^[-*] /.test(line)))
|
|
return (
|
|
<ul key={index}>
|
|
{lines.map((line, i) => (
|
|
<li key={i}>{inline(line.slice(2))}</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
if (lines.every((line) => /^\d+\. /.test(line)))
|
|
return (
|
|
<ol key={index}>
|
|
{lines.map((line, i) => (
|
|
<li key={i}>{inline(line.replace(/^\d+\. /, ""))}</li>
|
|
))}
|
|
</ol>
|
|
);
|
|
return <p key={index}>{inline(block)}</p>;
|
|
});
|
|
}
|
|
|
|
export default async function Docs({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ page?: string }>;
|
|
}) {
|
|
const { page = "welcome" } = await searchParams;
|
|
const { locale } = await getI18n();
|
|
const pages = [
|
|
...guides,
|
|
{
|
|
slug: "developer-api",
|
|
title: locale === "ru" ? "API для разработчиков" : "Developer API",
|
|
markdown: "",
|
|
},
|
|
];
|
|
const guide = pages.find((item) => item.slug === page);
|
|
if (!guide) notFound();
|
|
const index = pages.indexOf(guide);
|
|
return (
|
|
<>
|
|
<Header />
|
|
<main className={`section page-content ${styles.layout}`}>
|
|
<aside>
|
|
<h2>Player docs</h2>
|
|
<nav aria-label="Documentation pages" className={styles.menu}>
|
|
{pages.map((item) => (
|
|
<Link
|
|
key={item.slug}
|
|
href={`/docs?page=${item.slug}`}
|
|
aria-current={page === item.slug ? "page" : undefined}
|
|
>
|
|
{item.title}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
</aside>
|
|
<article className={`prose ${styles.article}`}>
|
|
{page === "developer-api" ? (
|
|
<DeveloperDocumentation locale={locale} />
|
|
) : (
|
|
content(guide.markdown)
|
|
)}
|
|
<div className={styles.paging}>
|
|
{index > 0 && (
|
|
<Link href={`/docs?page=${pages[index - 1].slug}`}>
|
|
← {pages[index - 1].title}
|
|
</Link>
|
|
)}
|
|
{index < pages.length - 1 && (
|
|
<Link href={`/docs?page=${pages[index + 1].slug}`}>
|
|
{pages[index + 1].title} →
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</article>
|
|
</main>
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|