287 lines
9.3 KiB
TypeScript
287 lines
9.3 KiB
TypeScript
import { getAllWikiPaths, getWikiContent } from "../../../lib/wiki/api";
|
|
import WikiPage from "../../../interfaces/WikiPage";
|
|
import DashLayout from "../../../layouts/DashLayout";
|
|
import Link from "next/link";
|
|
import { m } from "framer-motion";
|
|
import PageBody from "../../../components/wiki/PageBody";
|
|
import { ReactElement, useEffect, useState } from "react";
|
|
import Image from "next/image";
|
|
|
|
interface WikiLandingPageProps {
|
|
children: React.ReactNode;
|
|
page: WikiPage;
|
|
}
|
|
|
|
interface TableOfContentsItem {
|
|
id: string;
|
|
type: string;
|
|
text: string;
|
|
}
|
|
|
|
function WikiLandingPage(props: WikiLandingPageProps) {
|
|
const [wikiContent, setWikiContent] = useState<ReactElement>(<></>);
|
|
const [indexContent, setIndexContent] = useState<TableOfContentsItem[]>([]);
|
|
const [showMobileIndex, setShowMobileIndex] = useState<boolean>(false);
|
|
|
|
// needed for proper hydration due to replacing some elements
|
|
useEffect(() => {
|
|
setWikiContent(<PageBody page={props.page}>{props.page.content}</PageBody>);
|
|
}, [props.page]);
|
|
|
|
useEffect(() => {
|
|
const toc: TableOfContentsItem[] = [];
|
|
const headings = document.querySelectorAll("h2, h3");
|
|
// store the heading text, id, and type, keep order
|
|
headings.forEach((heading) => {
|
|
const id = heading.getAttribute("id");
|
|
const type = heading.tagName.toLowerCase();
|
|
const text = heading.textContent;
|
|
if (id && type && text) {
|
|
toc.push({ id, type, text });
|
|
}
|
|
});
|
|
setIndexContent(toc);
|
|
}, [wikiContent]);
|
|
|
|
const dirPath = props.page.path.split("/").map((path, index) => {
|
|
// if home page, don't show
|
|
if (path === "home") return <></>;
|
|
return (
|
|
<div key={path} className="flex flex-row">
|
|
<span className="mx-2 flex items-center text-white">
|
|
<m.svg
|
|
viewBox={"0 0 24 24"}
|
|
width={20}
|
|
height={20}
|
|
origin="center"
|
|
color="currentColor"
|
|
fill="currentColor"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<m.path d="M6.23 20.23 8 22l10-10L8 2 6.23 3.77 14.46 12z" />
|
|
</m.svg>
|
|
</span>
|
|
<Link
|
|
href={`/wiki/${props.page.language}/${props.page.path
|
|
.split("/")
|
|
.slice(0, index + 1)
|
|
.join("/")}`}
|
|
>
|
|
<p className="hover:text-orange-400">{path}</p>
|
|
</Link>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
return (
|
|
<div className="flex w-full flex-col items-center justify-center p-3 font-plusJakarta ">
|
|
<div className="inline-grid h-full w-full max-w-screen-2xl grid-cols-10">
|
|
{/* Desktop Header */}
|
|
<div className="col-span-10 mb-2 hidden grid-cols-10 lg:inline-grid">
|
|
{/* Title */}
|
|
<div className="col-span-2 flex items-center justify-center">
|
|
<div className="text-2xl text-white">
|
|
<p>toffee wiki</p>
|
|
</div>
|
|
<Image
|
|
className="h-auto w-auto"
|
|
src="/img/logo.webp"
|
|
alt="toffee logo"
|
|
width={64}
|
|
height={64}
|
|
/>
|
|
</div>
|
|
{/* Dir Path */}
|
|
<div className="col-span-8 ml-3 mb-3 mt-3 flex text-left text-xl">
|
|
<div className="flex w-auto flex-row items-center justify-start rounded-full bg-black p-2 px-4">
|
|
<Link href="/wiki">
|
|
<p className="hover:text-orange-400">home</p>
|
|
</Link>
|
|
{dirPath}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/* Sidebar */}
|
|
<div className="col-span-2 hidden w-full flex-col items-center justify-center lg:block">
|
|
<div className="w-full rounded-tl-2xl rounded-bl-2xl border-r-2 border-orange-400 border-opacity-60 bg-zinc-800 bg-opacity-70 p-6 text-left text-6xl text-white">
|
|
<div className="text-2xl">Contents</div>
|
|
<div className="mt-4 text-left text-orange-400">
|
|
{indexContent.map((item) => {
|
|
return (
|
|
// increase indent based on heading level
|
|
<div
|
|
style={{
|
|
paddingLeft: `${(parseInt(item.type[1]) - 2) * 1.25}rem`,
|
|
}}
|
|
className="text-xl"
|
|
key={item.id}
|
|
>
|
|
<Link href={`#${item.id}`}>
|
|
<p className="mt-2 overflow-hidden overflow-ellipsis whitespace-nowrap hover:text-white">
|
|
{item.text}
|
|
</p>
|
|
</Link>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-span-10 mb-6 lg:hidden">
|
|
{/* Dir Path Mobile */}
|
|
<div className="col-span-8 flex text-left text-xl">
|
|
<div className="flex w-auto flex-row items-center justify-start rounded-full bg-black p-2 px-4">
|
|
<Link href="/wiki">
|
|
<p className="hover:text-orange-400">home</p>
|
|
</Link>
|
|
{dirPath}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/* Mobile "Side"-bar */}
|
|
<div className="col-span-10 mb-6 lg:hidden">
|
|
<div className="w-full rounded-2xl rounded-tl-2xl bg-zinc-800 bg-opacity-70 p-6 text-left text-6xl text-white">
|
|
<div
|
|
className="flex cursor-pointer flex-row justify-between text-2xl"
|
|
onClick={() => setShowMobileIndex(!showMobileIndex)}
|
|
>
|
|
<div>Contents</div>
|
|
<m.svg
|
|
className="pointer-events-auto mt-2 ml-3 cursor-pointer lg:hidden"
|
|
origin="center"
|
|
width="20"
|
|
height="21"
|
|
viewBox="0 0 330 330"
|
|
x={0}
|
|
y={0}
|
|
animate={{ rotate: showMobileIndex ? 180 : 0 }}
|
|
>
|
|
<m.path
|
|
d="M325.607,79.393c-5.857-5.857-15.355-5.858-21.213,0.001l-139.39,139.393L25.607,79.393 c-5.857-5.857-15.355-5.858-21.213,0.001c-5.858,5.858-5.858,15.355,0,21.213l150.004,150c2.813,2.813,6.628,4.393,10.606,4.393 s7.794-1.581,10.606-4.394l149.996-150C331.465,94.749,331.465,85.251,325.607,79.393z"
|
|
fill="white"
|
|
stroke="white"
|
|
strokeWidth="15"
|
|
strokeLinecap="round"
|
|
/>
|
|
</m.svg>
|
|
</div>
|
|
|
|
<m.div
|
|
className="overflow-hidden text-left text-orange-400"
|
|
animate={{
|
|
height: showMobileIndex ? "auto" : 0,
|
|
marginTop: showMobileIndex ? "0.5rem" : 0,
|
|
}}
|
|
>
|
|
{indexContent.map((item) => {
|
|
return (
|
|
// increase indent based on heading level
|
|
<div
|
|
style={{
|
|
paddingLeft: `${(parseInt(item.type[1]) - 2) * 2}rem`,
|
|
}}
|
|
className="text-xl"
|
|
key={item.id}
|
|
>
|
|
<Link href={`#${item.id}`}>
|
|
<p className="mt-2 overflow-hidden overflow-ellipsis whitespace-nowrap hover:text-white">
|
|
{item.text}
|
|
</p>
|
|
</Link>
|
|
</div>
|
|
);
|
|
})}
|
|
</m.div>
|
|
</div>
|
|
</div>
|
|
{/* Main content */}
|
|
<div className="col-span-10 rounded-2xl bg-zinc-800 bg-opacity-70 px-6 pb-5 text-center text-6xl text-white lg:col-span-8 lg:rounded-tl-none">
|
|
<m.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: 0.3 }}
|
|
key={props.page.slug}
|
|
>
|
|
<div className="w-full px-3">{wikiContent}</div>
|
|
</m.div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
type Params = {
|
|
params: {
|
|
slug: string[];
|
|
};
|
|
};
|
|
|
|
export async function getStaticProps({ params }: Params) {
|
|
// only language
|
|
if (params.slug.length < 2) {
|
|
// if langauge is two letters, redirect to its home
|
|
if (params.slug[0].length === 2 && params.slug[0].match(/^[a-z]+$/i)) {
|
|
return {
|
|
redirect: {
|
|
destination: `/wiki/${params.slug[0]}/home`,
|
|
},
|
|
};
|
|
}
|
|
// else, 404 to prevemt building pointless pages (e.g. /wiki/this-is-not-a-language x 580258538 times)
|
|
return {
|
|
notFound: true,
|
|
};
|
|
}
|
|
// slug[0] = language
|
|
// slug[1...n] = page path
|
|
const lang = params.slug[0];
|
|
const path = params.slug.slice(1).join("/");
|
|
const pageData = getWikiContent(lang, path);
|
|
|
|
// if no content, 404
|
|
if (!pageData) {
|
|
return {
|
|
notFound: true,
|
|
};
|
|
}
|
|
|
|
return {
|
|
props: {
|
|
page: {
|
|
slug: params.slug,
|
|
content: pageData.content,
|
|
language: lang,
|
|
path: path,
|
|
data: pageData.data,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function getStaticPaths() {
|
|
const paths = getAllWikiPaths();
|
|
|
|
return {
|
|
paths: paths.map((path) => {
|
|
return {
|
|
params: {
|
|
slug: path.split("/"),
|
|
},
|
|
};
|
|
}),
|
|
fallback: "blocking",
|
|
};
|
|
}
|
|
|
|
WikiLandingPage.getLayout = function getLayout(page: React.ReactNode) {
|
|
const metaTags = {
|
|
title: "Wiki - toffee",
|
|
description: "Wiki for toffee",
|
|
};
|
|
return <DashLayout metaTags={metaTags}>{page}</DashLayout>;
|
|
};
|
|
|
|
export default WikiLandingPage;
|