From 730533e88e34cdd74d903629363efa89825a7ffd Mon Sep 17 00:00:00 2001
From: 3zachm <3zachm2@gmail.com>
Date: Tue, 7 Feb 2023 01:49:06 -0800
Subject: [PATCH] responsive design, url routing, image support

---
 components/wiki/PageBody.tsx       | 11 ++++-
 components/wiki/RenderMarkdown.tsx | 38 +++++++++++++++--
 interfaces/WikiPage.ts             |  2 +
 pages/wiki/[...slug]/index.tsx     | 68 ++++++++++++++++++++++++++++--
 4 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/components/wiki/PageBody.tsx b/components/wiki/PageBody.tsx
index 107e08c..ade3dff 100644
--- a/components/wiki/PageBody.tsx
+++ b/components/wiki/PageBody.tsx
@@ -3,14 +3,21 @@ import RenderMarkdown from "./RenderMarkdown";
 
 interface PageBodyProps {
   children: string;
+  currentLanguage: string;
+  path: string;
 }
 
-export default function PageBody({ children }: PageBodyProps) {
+export default function PageBody(props: PageBodyProps) {
   return (
     <div className="prose prose-sm sm:prose lg:prose-lg xl:prose-xl">
       <div className={mdStyles["markdown-body"]}>
         <div className="text-left">
-          <RenderMarkdown>{children}</RenderMarkdown>
+          <RenderMarkdown
+            path={props.path}
+            currentLanguage={props.currentLanguage}
+          >
+            {props.children}
+          </RenderMarkdown>
         </div>
       </div>
     </div>
diff --git a/components/wiki/RenderMarkdown.tsx b/components/wiki/RenderMarkdown.tsx
index 54891f2..4b62cda 100644
--- a/components/wiki/RenderMarkdown.tsx
+++ b/components/wiki/RenderMarkdown.tsx
@@ -7,24 +7,54 @@ import remarkGfm from "remark-gfm";
 
 interface RenderMarkdownProps {
   children: string;
+  path: string;
+  currentLanguage: string;
 }
 
-export default function RenderMarkdown({ children }: RenderMarkdownProps) {
+export default function RenderMarkdown(pageprops: RenderMarkdownProps) {
   return (
     <ReactMarkdown
       remarkPlugins={[remarkGfm]}
       rehypePlugins={[rehypeRaw, rehypeHighlight, rehypeSlug]}
       components={{
         a: ({ node, ...props }) => {
+          // if the link is internal, reformat it; if it ends with a slash, do not apply this
+          let href = props.href as string;
+          if (!href.endsWith("/") && !href.startsWith("http")) {
+            if (href.startsWith("/wiki/")) {
+              href = `/wiki/${pageprops.currentLanguage}${href.slice(5)}`;
+            } else {
+              // if single relative
+              href = `/wiki/${pageprops.currentLanguage}/${pageprops.path}/${href}`;
+            }
+          }
           return (
-            <Link legacyBehavior href={props.href as string}>
-              <a>{props.children ? props.children[0] : props.href}</a>
+            <Link legacyBehavior href={href as string}>
+              <a>{props.children ? props.children[0] : href}</a>
             </Link>
           );
         },
+        img: ({ node, ...props }) => {
+          // if image is internal (relative), prefix it with the current page's path
+          let src = props.src as string;
+          if (!src.startsWith("http") && !src.startsWith("/")) {
+            src = `/img/wiki/${pageprops.path}/${src}`;
+          }
+          return (
+            <div className="flex w-full flex-col items-center justify-center">
+              <img
+                className="mb-2"
+                src={src}
+                alt={props.alt as string}
+                title={props.title as string}
+              />
+              <p> {props.title as string} </p>
+            </div>
+          );
+        },
       }}
     >
-      {children}
+      {pageprops.children}
     </ReactMarkdown>
   );
 }
diff --git a/interfaces/WikiPage.ts b/interfaces/WikiPage.ts
index f09de46..f3ecbd4 100644
--- a/interfaces/WikiPage.ts
+++ b/interfaces/WikiPage.ts
@@ -2,6 +2,8 @@ export default interface WikiPage {
   slug: string;
   layout?: string;
   content: string;
+  language: string;
+  path: string;
   data: {
     layout?: string;
   };
diff --git a/pages/wiki/[...slug]/index.tsx b/pages/wiki/[...slug]/index.tsx
index 9e48730..8bdd9cd 100644
--- a/pages/wiki/[...slug]/index.tsx
+++ b/pages/wiki/[...slug]/index.tsx
@@ -20,11 +20,16 @@ interface TableOfContentsItem {
 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>{props.page.content}</PageBody>);
-  }, [props.page.content]);
+    setWikiContent(
+      <PageBody currentLanguage={props.page.language} path={props.page.path}>
+        {props.page.content}
+      </PageBody>
+    );
+  }, [props.page.content, props.page.language, props.page.path]);
 
   useEffect(() => {
     const toc: TableOfContentsItem[] = [];
@@ -39,7 +44,6 @@ function WikiLandingPage(props: WikiLandingPageProps) {
       }
     });
     setIndexContent(toc);
-    console.log(toc);
   }, [wikiContent]);
 
   return (
@@ -71,6 +75,62 @@ function WikiLandingPage(props: WikiLandingPageProps) {
             </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 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 p-6 text-center text-6xl text-white lg:col-span-8 lg:rounded-tl-none">
           <m.div
             initial={{ opacity: 0 }}
@@ -126,6 +186,8 @@ export async function getStaticProps({ params }: Params) {
       page: {
         slug: params.slug,
         content: pageData.content,
+        language: lang,
+        path: path,
         data: pageData.data,
       },
     },