From 57399a60dd9171c45a0e78b62db2137c6a4fd8b7 Mon Sep 17 00:00:00 2001
From: DrMint <thomas.barillot@etu.u-bordeaux.fr>
Date: Sat, 19 Feb 2022 02:14:16 +0100
Subject: [PATCH] Added language selector + better use of components

---
 src/components/AppLayout.tsx                 |  62 +++++-
 src/components/Button.tsx                    |  12 +-
 src/components/Chip.tsx                      |   6 +-
 src/components/InsetBox.tsx                  |  16 ++
 src/components/PanelComponents/NavOption.tsx |   2 +-
 src/components/Panels/MainPanel.tsx          |   3 +-
 src/graphql/operation.graphql                | 222 +++++++++----------
 src/graphql/operations-types.ts              |   1 +
 src/pages/library/items/[slug].tsx           |  14 +-
 src/pages/wiki/chronology.tsx                |  72 ++++--
 src/queries/helpers.ts                       |  23 +-
 src/tailwind.css                             |   6 +-
 tailwind.config.js                           |   2 +-
 13 files changed, 279 insertions(+), 162 deletions(-)
 create mode 100644 src/components/InsetBox.tsx

diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx
index b11b12f..327c857 100644
--- a/src/components/AppLayout.tsx
+++ b/src/components/AppLayout.tsx
@@ -3,6 +3,9 @@ import MainPanel from "./Panels/MainPanel";
 import { useState } from "react";
 import Head from "next/head";
 import { useSwipeable } from "react-swipeable";
+import { useRouter } from "next/router";
+import Button from "components/Button";
+import { prettyLanguage } from "queries/helpers";
 
 type AppLayoutProps = {
   subPanel?: React.ReactNode;
@@ -14,9 +17,11 @@ type AppLayoutProps = {
 
 export default function AppLayout(props: AppLayoutProps): JSX.Element {
   const titlePrefix = "Accord’s Library";
+  const router = useRouter();
 
   const [mainPanelOpen, setMainPanelOpen] = useState(false);
-  const [subPanelOpen, setsubPanelOpen] = useState(false);
+  const [subPanelOpen, setSubPanelOpen] = useState(false);
+  const [languagePanelOpen, setLanguagePanelOpen] = useState(false);
   const sensibilitySwipe = 1.1;
 
   const handlers = useSwipeable({
@@ -25,13 +30,13 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
       if (mainPanelOpen) {
         setMainPanelOpen(false);
       } else if (props.subPanel && props.contentPanel) {
-        setsubPanelOpen(true);
+        setSubPanelOpen(true);
       }
     },
     onSwipedRight: (SwipeEventData) => {
       if (SwipeEventData.velocity < sensibilitySwipe) return;
       if (subPanelOpen) {
-        setsubPanelOpen(false);
+        setSubPanelOpen(false);
       } else {
         setMainPanelOpen(true);
       }
@@ -74,7 +79,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
         <p className="text-2xl font-black font-headers">{props.title}</p>
         <span
           className="material-icons mt-[.1em] cursor-pointer"
-          onClick={() => setsubPanelOpen(true)}
+          onClick={() => setSubPanelOpen(true)}
         >
           {props.subPanel && !turnSubIntoContent
             ? props.subPanelIcon
@@ -110,12 +115,12 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
         ${turnSubIntoContent ? "z-10" : ""}
         ${
           mainPanelOpen || subPanelOpen
-            ? " opacity-50"
-            : "opacity-0 translate-x-full"
+            ? "opacity-50"
+            : "opacity-0 pointer-events-none touch-none"
         }`}
         onClick={() => {
           setMainPanelOpen(false);
-          setsubPanelOpen(false);
+          setSubPanelOpen(false);
         }}
       ></div>
 
@@ -142,7 +147,48 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
         className={`${mainPanelClass} border-r-[1px] border-black border-dotted top-0 bottom-0 left-0 right-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 z-20 bg-light bg-paper bg-blend-multiply bg-local bg-[length:10cm]
         ${mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
       >
-        <MainPanel langui={props.langui} />
+        <MainPanel
+          langui={props.langui}
+          setLanguagePanelOpen={setLanguagePanelOpen}
+        />
+      </div>
+
+      {/* Language selection background */}
+      <div
+        className={`fixed bg-dark inset-0 transition-all duration-500 z-20 grid place-content-center ${
+          languagePanelOpen
+            ? "bg-opacity-50"
+            : "bg-opacity-0 pointer-events-none touch-none"
+        }`}
+        onClick={() => {
+          setLanguagePanelOpen(false);
+        }}
+      >
+        <div
+          className={`p-10 bg-light rounded-lg shadow-2xl shadow-dark grid gap-4 place-items-center transition-transform ${
+            languagePanelOpen ? "scale-100" : "scale-0"
+          }`}
+        >
+          <h2 className="text-2xl">Select a language</h2>
+          <div className="flex flex-wrap flex-row gap-2">
+            {router.locales?.sort().map((locale) => (
+              <>
+                {locale !== "xx" ? (
+                  <Button
+                    key={locale}
+                    active={locale === router.locale}
+                    href={router.asPath}
+                    locale={locale}
+                  >
+                    {prettyLanguage(locale)}
+                  </Button>
+                ) : (
+                  ""
+                )}
+              </>
+            ))}
+          </div>
+        </div>
       </div>
     </div>
   );
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 772dd0e..9f87826 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -4,19 +4,27 @@ type ButtonProps = {
   className?: string;
   href?: string;
   children: React.ReactChild | React.ReactChild[];
+  active?: boolean;
+  locale?: string;
 };
 
 export default function Button(props: ButtonProps): JSX.Element {
   const button = (
     <div
-      className={`grid place-content-center place-items-center border-[1px] border-dark text-dark rounded-full cursor-pointer px-4 pt-[0.4rem] pb-[0.5rem] transition-all hover:text-light hover:bg-dark hover:drop-shadow-dark-lg active:bg-black active:drop-shadow-black-lg active:border-black ${props.className}`}
+      className={`grid place-content-center place-items-center border-[1px] border-dark text-dark rounded-full px-4 pt-[0.4rem] pb-[0.5rem] transition-all  ${
+        props.className
+      } ${
+        props.active
+          ? "text-light bg-black drop-shadow-black-lg !border-black cursor-not-allowed"
+          : "cursor-pointer hover:text-light hover:bg-dark hover:drop-shadow-dark-lg active:bg-black active:drop-shadow-black-lg active:border-black"
+      }`}
     >
       {props.children}
     </div>
   );
 
   const result = props.href ? (
-    <Link href={props.href} passHref>
+    <Link href={props.href} locale={props.locale} passHref>
       {button}
     </Link>
   ) : (
diff --git a/src/components/Chip.tsx b/src/components/Chip.tsx
index 449ee26..20b4c6b 100644
--- a/src/components/Chip.tsx
+++ b/src/components/Chip.tsx
@@ -6,11 +6,7 @@ type ChipProps = {
 export default function Chip(props: ChipProps): JSX.Element {
   return (
     <div
-      className={
-        "grid place-content-center place-items-center text-xs pb-[0.14rem] px-1.5 border-[1px] rounded-full opacity-70" +
-        " " +
-        props.className
-      }
+      className={`grid place-content-center place-items-center text-xs pb-[0.14rem] px-1.5 border-[1px] rounded-full opacity-70 ${props.className}`}
     >
       {props.children}
     </div>
diff --git a/src/components/InsetBox.tsx b/src/components/InsetBox.tsx
new file mode 100644
index 0000000..62b6641
--- /dev/null
+++ b/src/components/InsetBox.tsx
@@ -0,0 +1,16 @@
+type InsetBoxProps = {
+  className?: string;
+  children: React.ReactChild | React.ReactChild[];
+  id?: string;
+};
+
+export default function InsetBox(props: InsetBoxProps): JSX.Element {
+  return (
+    <div
+      id={props.id}
+      className={`w-full shadow-inner-sm shadow-dark bg-mid rounded-xl p-8 ${props.className}`}
+    >
+      {props.children}
+    </div>
+  );
+}
diff --git a/src/components/PanelComponents/NavOption.tsx b/src/components/PanelComponents/NavOption.tsx
index 898baf5..4189b40 100644
--- a/src/components/PanelComponents/NavOption.tsx
+++ b/src/components/PanelComponents/NavOption.tsx
@@ -15,7 +15,7 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
   const divActive = "bg-mid shadow-inner-sm shadow-dark";
   const border =
     "outline outline-mid outline-2 outline-offset-[-2px] hover:outline-[transparent]";
-  const divCommon = `gap-x-5 w-full rounded-2xl cursor-pointer p-4 hover:bg-mid hover:shadow-inner-sm hover:shadow-dark active:shadow-inner active:shadow-dark transition-all ${
+  const divCommon = `gap-x-5 w-full rounded-2xl cursor-pointer p-4 hover:bg-mid hover:shadow-inner-sm hover:shadow-dark hover:active:shadow-inner hover:active:shadow-dark transition-all ${
     props.border ? border : ""
   } ${isActive ? divActive : ""}`;
 
diff --git a/src/components/Panels/MainPanel.tsx b/src/components/Panels/MainPanel.tsx
index 8685276..94746ec 100644
--- a/src/components/Panels/MainPanel.tsx
+++ b/src/components/Panels/MainPanel.tsx
@@ -9,6 +9,7 @@ import Markdown from "markdown-to-jsx";
 
 type MainPanelProps = {
   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
+  setLanguagePanelOpen: Function;
 };
 
 export default function MainPanel(props: MainPanelProps): JSX.Element {
@@ -29,7 +30,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
               />
             </div>
           </Link>
-          <div className="relative mt-5">
+          <div className="relative mt-5" onClick={() => props.setLanguagePanelOpen(true)}>
             {router.locale ? (
               <Button className="absolute right-0 top-[-1.3em] text-xs !py-0.5 !px-2.5">
                 {router.locale.toUpperCase()}
diff --git a/src/graphql/operation.graphql b/src/graphql/operation.graphql
index b6c830b..96ef853 100644
--- a/src/graphql/operation.graphql
+++ b/src/graphql/operation.graphql
@@ -82,6 +82,7 @@ query getEras($language_code: String) {
         ending_year
         title(filters: { language: { code: { eq: $language_code } } }) {
           title
+          description
         }
       }
     }
@@ -124,119 +125,118 @@ query getChronologyItems($language_code: String) {
 }
 
 query getLibraryItemsPreview($language_code: String) {
-	libraryItems(
-		filters: { root_item: { eq: true } }
-		pagination: { limit: -1 }
-		sort: ["title:asc", "subtitle:asc"]
-	) {
-		data {
-			id
-			attributes {
-				title
-				subtitle
-				slug
-				thumbnail {
-					data {
-						attributes {
-							name
-							alternativeText
-							caption
-							width
-							height
-							url
-						}
-					}
-				}
-				release_date {
-					year
-					month
-					day
-				}
-				price {
-					amount
-					currency {
-						data {
-							attributes {
-								symbol
-								code
-							}
-						}
-					}
-				}
-				metadata {
-					__typename
-					... on ComponentMetadataBooks {
-						subtype {
-							data {
-								attributes {
-									slug
-									titles(
-										filters: { language: { code: { eq: $language_code } } }
-									) {
-										title
-									}
-								}
-							}
-						}
-					}
-					... on ComponentMetadataGame {
-						platform {
-							data {
-								attributes {
-									short
-								}
-							}
-						}
-					}
-					... on ComponentMetadataVideo {
-						subtype {
-							data {
-								attributes {
-									slug
-									titles(
-										filters: { language: { code: { eq: $language_code } } }
-									) {
-										title
-									}
-								}
-							}
-						}
-					}
-					... on ComponentMetadataAudio {
-						subtype {
-							data {
-								attributes {
-									slug
-									titles(
-										filters: { language: { code: { eq: $language_code } } }
-									) {
-										title
-									}
-								}
-							}
-						}
-					}
-					... on ComponentMetadataOther {
-						subtype {
-							data {
-								attributes {
-									slug
-									titles(
-										filters: { language: { code: { eq: $language_code } } }
-									) {
-										title
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
+  libraryItems(
+    filters: { root_item: { eq: true } }
+    pagination: { limit: -1 }
+    sort: ["title:asc", "subtitle:asc"]
+  ) {
+    data {
+      id
+      attributes {
+        title
+        subtitle
+        slug
+        thumbnail {
+          data {
+            attributes {
+              name
+              alternativeText
+              caption
+              width
+              height
+              url
+            }
+          }
+        }
+        release_date {
+          year
+          month
+          day
+        }
+        price {
+          amount
+          currency {
+            data {
+              attributes {
+                symbol
+                code
+              }
+            }
+          }
+        }
+        metadata {
+          __typename
+          ... on ComponentMetadataBooks {
+            subtype {
+              data {
+                attributes {
+                  slug
+                  titles(
+                    filters: { language: { code: { eq: $language_code } } }
+                  ) {
+                    title
+                  }
+                }
+              }
+            }
+          }
+          ... on ComponentMetadataGame {
+            platform {
+              data {
+                attributes {
+                  short
+                }
+              }
+            }
+          }
+          ... on ComponentMetadataVideo {
+            subtype {
+              data {
+                attributes {
+                  slug
+                  titles(
+                    filters: { language: { code: { eq: $language_code } } }
+                  ) {
+                    title
+                  }
+                }
+              }
+            }
+          }
+          ... on ComponentMetadataAudio {
+            subtype {
+              data {
+                attributes {
+                  slug
+                  titles(
+                    filters: { language: { code: { eq: $language_code } } }
+                  ) {
+                    title
+                  }
+                }
+              }
+            }
+          }
+          ... on ComponentMetadataOther {
+            subtype {
+              data {
+                attributes {
+                  slug
+                  titles(
+                    filters: { language: { code: { eq: $language_code } } }
+                  ) {
+                    title
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
 }
 
-
 query getLibraryItemsSlugs {
   libraryItems(pagination: { limit: -1 }) {
     data {
diff --git a/src/graphql/operations-types.ts b/src/graphql/operations-types.ts
index 3ab1c72..749d3be 100644
--- a/src/graphql/operations-types.ts
+++ b/src/graphql/operations-types.ts
@@ -165,6 +165,7 @@ export type GetErasQuery = {
         title: Array<{
           __typename: "ComponentTranslationsChronologyEra";
           title: string;
+          description: string;
         }>;
       };
     }>;
diff --git a/src/pages/library/items/[slug].tsx b/src/pages/library/items/[slug].tsx
index dfce15a..18e41ed 100644
--- a/src/pages/library/items/[slug].tsx
+++ b/src/pages/library/items/[slug].tsx
@@ -32,6 +32,7 @@ import Button from "components/Button";
 import HorizontalLine from "components/HorizontalLine";
 import AppLayout from "components/AppLayout";
 import LibraryItemsPreview from "components/Library/LibraryItemsPreview";
+import InsetBox from "components/InsetBox";
 
 type LibrarySlugProps = {
   libraryItem: GetLibraryItemQuery;
@@ -124,10 +125,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
           )}
         </div>
 
-        <div
-          id="summary"
-          className="bg-mid w-full grid place-items-center p-8 rounded-2xl shadow-inner-sm shadow-dark"
-        >
+        <InsetBox id="summary" className="grid place-items-center">
           <div className="w-[clamp(0px,100%,42rem)] grid place-items-center gap-8">
             {item.subitem_of.data.length > 0 ? (
               <div className="grid place-items-center">
@@ -159,7 +157,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
               ""
             )}
           </div>
-        </div>
+        </InsetBox>
 
         {item.gallery.data.length > 0 ? (
           <div id="gallery" className="grid place-items-center gap-8  w-full">
@@ -188,9 +186,9 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
           ""
         )}
 
-        <div
+        <InsetBox
           id="details"
-          className="bg-mid w-full grid place-items-center p-8 rounded-2xl text-left shadow-inner-sm shadow-dark"
+          className="grid place-items-center"
         >
           <div className="w-[clamp(0px,100%,42rem)] grid place-items gap-8">
             <h2 className="text-2xl text-center">
@@ -345,7 +343,7 @@ export default function LibrarySlug(props: LibrarySlugProps): JSX.Element {
               ""
             )}
           </div>
-        </div>
+        </InsetBox>
 
         {item.subitems.data.length > 0 ? (
           item.metadata.length > 0 &&
diff --git a/src/pages/wiki/chronology.tsx b/src/pages/wiki/chronology.tsx
index cd5203f..78f5969 100644
--- a/src/pages/wiki/chronology.tsx
+++ b/src/pages/wiki/chronology.tsx
@@ -16,6 +16,8 @@ import NavOption from "components/PanelComponents/NavOption";
 import ReturnButton from "components/PanelComponents/ReturnButton";
 import HorizontalLine from "components/HorizontalLine";
 import AppLayout from "components/AppLayout";
+import { prettySlug } from "queries/helpers";
+import InsetBox from "components/InsetBox";
 
 interface DataChronologyProps {
   chronologyItems: GetChronologyItemsQuery;
@@ -27,20 +29,39 @@ export default function DataChronology(
   props: DataChronologyProps
 ): JSX.Element {
   const langui = props.langui.websiteInterfaces.data[0].attributes;
+  const chronologyItems = props.chronologyItems.chronologyItems;
+  const chronologyEras = props.chronologyEras.chronologyEras;
 
   // Group by year the Chronology items
-  let chronologyItemYearGroups: GetChronologyItemsQuery["chronologyItems"]["data"][number][][] =
+  let chronologyItemYearGroups: GetChronologyItemsQuery["chronologyItems"]["data"][number][][][] =
     [];
 
-  if (props.chronologyItems.chronologyItems) {
-    props.chronologyItems.chronologyItems.data.map((item) => {
-      if (!chronologyItemYearGroups.hasOwnProperty(item.attributes.year)) {
-        chronologyItemYearGroups[item.attributes.year] = [item];
-      } else {
-        chronologyItemYearGroups[item.attributes.year].push(item);
-      }
-    });
-  }
+  chronologyEras.data.map((era) => {
+    chronologyItemYearGroups.push([]);
+  });
+
+  let currentChronologyEraIndex = 0;
+  chronologyItems.data.map((item) => {
+    if (
+      item.attributes.year >
+      chronologyEras.data[currentChronologyEraIndex].attributes.ending_year
+    ) {
+      currentChronologyEraIndex++;
+    }
+    if (
+      !chronologyItemYearGroups[currentChronologyEraIndex].hasOwnProperty(
+        item.attributes.year
+      )
+    ) {
+      chronologyItemYearGroups[currentChronologyEraIndex][
+        item.attributes.year
+      ] = [item];
+    } else {
+      chronologyItemYearGroups[currentChronologyEraIndex][
+        item.attributes.year
+      ].push(item);
+    }
+  });
 
   const subPanel = (
     <SubPanel>
@@ -51,7 +72,7 @@ export default function DataChronology(
         <NavOption
           key={era.id}
           url={"#" + era.attributes.slug}
-          title={era.attributes.title[0] ? era.attributes.title[0].title : ""}
+          title={era.attributes.title.length > 0 ? era.attributes.title[0].title : prettySlug(era.attributes.slug)}
           subtitle={
             era.attributes.starting_year + " → " + era.attributes.ending_year
           }
@@ -63,17 +84,32 @@ export default function DataChronology(
 
   const contentPanel = (
     <ContentPanel>
-      {chronologyItemYearGroups.map((items, index: number) => {
-        if (items && items[0].attributes.year) {
-          return (
+      {chronologyItemYearGroups.map((era, eraIndex) => (
+        <>
+          <InsetBox
+            id={chronologyEras.data[eraIndex].attributes.slug}
+            className="grid text-center my-8 gap-4"
+          >
+            <h2 className="text-2xl">
+              {chronologyEras.data[eraIndex].attributes.title.length > 0
+                ? chronologyEras.data[eraIndex].attributes.title[0].title
+                : prettySlug(chronologyEras.data[eraIndex].attributes.slug)}
+            </h2>
+            <p className="whitespace-pre-line ">
+              {chronologyEras.data[eraIndex].attributes.title.length > 0
+                ? chronologyEras.data[eraIndex].attributes.title[0].description
+                : ""}
+            </p>
+          </InsetBox>
+          {era.map((items, index) => (
             <ChronologyYearComponent
-              key={index}
+              key={`${eraIndex}-${index}`}
               year={items[0].attributes.year}
               items={items}
             />
-          );
-        }
-      })}
+          ))}
+        </>
+      ))}
     </ContentPanel>
   );
 
diff --git a/src/queries/helpers.ts b/src/queries/helpers.ts
index 4dc1a66..7bbfe01 100644
--- a/src/queries/helpers.ts
+++ b/src/queries/helpers.ts
@@ -51,7 +51,7 @@ export function prettyinlineTitle(
 
 export function prettyItemType(
   metadata: {
-    __typename: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number]["__typename"]
+    __typename: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number]["__typename"];
   },
   langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"]
 ): string {
@@ -73,7 +73,7 @@ export function prettyItemType(
 }
 
 export function prettyItemSubType(
-  metadata: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number],
+  metadata: GetLibraryItemsPreviewQuery["libraryItems"]["data"][number]["attributes"]["metadata"][number]
 ): string {
   switch (metadata.__typename) {
     case "ComponentMetadataAudio":
@@ -92,6 +92,25 @@ export function prettyItemSubType(
   }
 }
 
+export function prettyLanguage(code: string): string {
+  switch (code) {
+    case "en":
+      return "English";
+    case "es":
+      return "Español";
+    case "fr":
+      return "Français";
+    case "ja":
+      return "日本語";
+    case "en":
+      return "English";
+    case "xx":
+      return "██";
+    default:
+      return code;
+  }
+}
+
 export function capitalizeString(string: string): string {
   function capitalizeWord(word: string): string {
     return word.charAt(0).toUpperCase() + word.substring(1);
diff --git a/src/tailwind.css b/src/tailwind.css
index 40f1863..f8d407c 100644
--- a/src/tailwind.css
+++ b/src/tailwind.css
@@ -9,7 +9,7 @@
   }
 
   * {
-    @apply box-border font-body font-medium scroll-smooth;
+    @apply box-border font-body font-medium scroll-smooth scroll-m-8;
   }
 
   h1,
@@ -25,10 +25,6 @@
     @apply transition-colors underline-offset-2 decoration-dotted underline decoration-dark hover:text-dark;
   }
 
-  *:target {
-    @apply scroll-mt-4;
-  }
-
   *::selection {
     @apply bg-dark text-light;
   }
diff --git a/tailwind.config.js b/tailwind.config.js
index 692398b..ea65354 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -79,7 +79,7 @@ module.exports = {
         ".drop-shadow-dark-2xl": {
           filter: "drop-shadow(0 25px 25px rgb(156 102 68 / 0.8))",
         },
-        
+
         ".drop-shadow-black-lg": {
           filter:
             "drop-shadow(0 10px 8px rgb(27 24 17 / 0.2)) drop-shadow(0 4px 3px rgb(27 24 17 / 0.4))",