2022-04-10 23:18:52 +02:00

431 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Button from "components/Button";
import { useAppLayout } from "contexts/AppLayoutContext";
import { UploadImageFragment } from "graphql/generated";
import { useMediaMobile } from "hooks/useMediaQuery";
import Head from "next/head";
import { useRouter } from "next/router";
import { AppStaticProps } from "queries/getAppStaticProps";
import {
getOgImage,
OgImage,
prettyLanguage,
prettySlug,
} from "queries/helpers";
import { useEffect, useState } from "react";
import { useSwipeable } from "react-swipeable";
import { ImageQuality } from "./Img";
import OrderableList from "./OrderableList";
import MainPanel from "./Panels/MainPanel";
import Popup from "./Popup";
import Select from "./Select";
interface Props extends AppStaticProps {
subPanel?: React.ReactNode;
subPanelIcon?: string;
contentPanel?: React.ReactNode;
title?: string;
navTitle: string | null | undefined;
thumbnail?: UploadImageFragment;
description?: string;
}
export default function AppLayout(props: Props): JSX.Element {
const { langui, currencies, languages, subPanel, contentPanel } = props;
const router = useRouter();
const isMobile = useMediaMobile();
const appLayout = useAppLayout();
const sensibilitySwipe = 1.1;
const handlers = useSwipeable({
onSwipedLeft: (SwipeEventData) => {
if (SwipeEventData.velocity < sensibilitySwipe) return;
if (appLayout.mainPanelOpen) {
appLayout.setMainPanelOpen(false);
} else if (subPanel && contentPanel) {
appLayout.setSubPanelOpen(true);
}
},
onSwipedRight: (SwipeEventData) => {
if (SwipeEventData.velocity < sensibilitySwipe) return;
if (appLayout.subPanelOpen) {
appLayout.setSubPanelOpen(false);
} else {
appLayout.setMainPanelOpen(true);
}
},
});
const turnSubIntoContent = subPanel && !contentPanel;
const titlePrefix = "Accords Library";
const metaImage: OgImage = props.thumbnail
? getOgImage(ImageQuality.Og, props.thumbnail)
: {
image: "/default_og.jpg",
width: 1200,
height: 630,
alt: "Accord's Library Logo",
};
const ogTitle =
props.title ?? props.navTitle ?? prettySlug(router.asPath.split("/").pop());
const metaDescription = props.description ?? langui.default_description ?? "";
useEffect(() => {
document.getElementsByTagName("html")[0].style.fontSize = `${
(appLayout.fontSize ?? 1) * 100
}%`;
}, [appLayout.fontSize]);
const currencyOptions: string[] = [];
currencies.map((currency) => {
if (currency.attributes?.code)
currencyOptions.push(currency.attributes.code);
});
const [currencySelect, setCurrencySelect] = useState<number>(-1);
useEffect(() => {
if (appLayout.currency)
setCurrencySelect(currencyOptions.indexOf(appLayout.currency));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appLayout.currency]);
useEffect(() => {
if (currencySelect >= 0)
appLayout.setCurrency(currencyOptions[currencySelect]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currencySelect]);
let gridCol = "";
if (props.subPanel) {
if (appLayout.mainPanelReduced) {
gridCol = "grid-cols-[6rem_20rem_1fr]";
} else {
gridCol = "grid-cols-[20rem_20rem_1fr]";
}
} else if (appLayout.mainPanelReduced) {
gridCol = "grid-cols-[6rem_0px_1fr]";
} else {
gridCol = "grid-cols-[20rem_0px_1fr]";
}
return (
<div
id="MyAppLayout"
className={`${
appLayout.darkMode ? "set-theme-dark" : "set-theme-light"
} ${
appLayout.dyslexic
? "set-theme-font-dyslexic"
: "set-theme-font-standard"
}`}
>
<div
{...handlers}
className={`fixed inset-0 touch-pan-y p-0 m-0 bg-light text-black grid [grid-template-areas:'main_sub_content'] ${gridCol} mobile:grid-cols-[1fr] mobile:grid-rows-[1fr_5rem] mobile:[grid-template-areas:'content''navbar']`}
>
<Head>
<title>{`${titlePrefix} - ${ogTitle}`}</title>
<meta
name="twitter:title"
content={`${titlePrefix} - ${ogTitle}`}
></meta>
<meta name="description" content={metaDescription} />
<meta name="twitter:description" content={metaDescription}></meta>
<meta property="og:image" content={metaImage.image}></meta>
<meta property="og:image:secure_url" content={metaImage.image}></meta>
<meta
property="og:image:width"
content={metaImage.width.toString()}
></meta>
<meta
property="og:image:height"
content={metaImage.height.toString()}
></meta>
<meta property="og:image:alt" content={metaImage.alt}></meta>
<meta property="og:image:type" content="image/jpeg"></meta>
<meta name="twitter:card" content="summary_large_image"></meta>
<meta name="twitter:image" content={metaImage.image}></meta>
</Head>
{/* Background when navbar is opened */}
<div
className={`[grid-area:content] mobile:z-10 absolute inset-0 transition-[backdrop-filter] duration-500 ${
(appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile
? "[backdrop-filter:blur(2px)]"
: "pointer-events-none touch-none "
}`}
>
<div
className={`absolute bg-shade inset-0 transition-opacity duration-500
${turnSubIntoContent ? "" : ""}
${
(appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile
? "opacity-60"
: "opacity-0"
}`}
onClick={() => {
appLayout.setMainPanelOpen(false);
appLayout.setSubPanelOpen(false);
}}
></div>
</div>
{/* Content panel */}
<div
className={`[grid-area:content] overflow-y-scroll bg-light texture-paper-dots`}
>
{contentPanel ? (
contentPanel
) : (
<div className="grid place-content-center h-full">
<div className="text-dark border-dark border-2 border-dotted rounded-2xl p-8 grid grid-flow-col place-items-center gap-9 opacity-40">
<p className="text-4xl"></p>
<p className="text-2xl w-64">{langui.select_option_sidebar}</p>
</div>
</div>
)}
</div>
{/* Sub panel */}
{subPanel && (
<div
className={`[grid-area:sub] mobile:[grid-area:content] mobile:z-10 mobile:w-[90%] mobile:justify-self-end border-r-[1px] mobile:border-r-0 mobile:border-l-[1px] border-black border-dotted overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 bg-light texture-paper-dots
${
turnSubIntoContent
? "mobile:border-l-0 mobile:w-full"
: !appLayout.subPanelOpen && "mobile:translate-x-[100vw]"
}`}
>
{subPanel}
</div>
)}
{/* Main panel */}
<div
className={`[grid-area:main] mobile:[grid-area:content] mobile:z-10 mobile:w-[90%] mobile:justify-self-start border-r-[1px] border-black border-dotted overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 bg-light texture-paper-dots
${appLayout.mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
>
<MainPanel langui={langui} />
</div>
{/* Navbar */}
<div className="[grid-area:navbar] border-t-[1px] border-black border-dotted grid grid-cols-[5rem_1fr_5rem] place-items-center desktop:hidden bg-light texture-paper-dots">
<span
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => {
appLayout.setMainPanelOpen(!appLayout.mainPanelOpen);
appLayout.setSubPanelOpen(false);
}}
>
{appLayout.mainPanelOpen ? "close" : "menu"}
</span>
<p
className={`font-black font-headers text-center overflow-hidden ${
ogTitle && ogTitle.length > 30
? "text-xl max-h-14"
: "text-2xl max-h-16"
}`}
>
{ogTitle}
</p>
<span
className="material-icons mt-[.1em] cursor-pointer"
onClick={() => {
appLayout.setSubPanelOpen(!appLayout.subPanelOpen);
appLayout.setMainPanelOpen(false);
}}
>
{subPanel && !turnSubIntoContent
? appLayout.subPanelOpen
? "close"
: props.subPanelIcon
? props.subPanelIcon
: "tune"
: ""}
</span>
</div>
<Popup
state={appLayout.configPanelOpen}
setState={appLayout.setConfigPanelOpen}
>
<h2 className="text-2xl">{langui.settings}</h2>
<div className="mt-4 grid gap-16 justify-items-center text-center desktop:grid-cols-[auto_auto]">
{router.locales && (
<div>
<h3 className="text-xl">{langui.languages}</h3>
{appLayout.preferredLanguages && (
<OrderableList
items={
appLayout.preferredLanguages.length > 0
? new Map(
appLayout.preferredLanguages.map((locale) => [
locale,
prettyLanguage(locale, languages),
])
)
: new Map(
router.locales.map((locale) => [
locale,
prettyLanguage(locale, languages),
])
)
}
onChange={(items) => {
const preferredLanguages = [...items].map(
([code]) => code
);
console.log(router.asPath);
appLayout.setPreferredLanguages(preferredLanguages);
router.push(router.asPath, router.asPath, {
locale: preferredLanguages[0],
});
}}
/>
)}
</div>
)}
<div className="grid gap-8 place-items-center text-center desktop:grid-cols-2">
<div>
<h3 className="text-xl">{langui.theme}</h3>
<div className="flex flex-row">
<Button
onClick={() => {
appLayout.setDarkMode(false);
appLayout.setSelectedThemeMode(true);
}}
active={
appLayout.selectedThemeMode === true &&
appLayout.darkMode === false
}
className="rounded-r-none"
>
{langui.light}
</Button>
<Button
onClick={() => {
appLayout.setSelectedThemeMode(false);
}}
active={appLayout.selectedThemeMode === false}
className="rounded-l-none rounded-r-none border-x-0"
>
{langui.auto}
</Button>
<Button
onClick={() => {
appLayout.setDarkMode(true);
appLayout.setSelectedThemeMode(true);
}}
active={
appLayout.selectedThemeMode === true &&
appLayout.darkMode === true
}
className="rounded-l-none"
>
{langui.dark}
</Button>
</div>
</div>
<div>
<h3 className="text-xl">{langui.currency}</h3>
<div>
<Select
options={currencyOptions}
state={currencySelect}
setState={setCurrencySelect}
className="w-28"
/>
</div>
</div>
<div>
<h3 className="text-xl">{langui.font_size}</h3>
<div className="flex flex-row">
<Button
className="rounded-r-none"
onClick={() =>
appLayout.setFontSize(
appLayout.fontSize
? appLayout.fontSize / 1.05
: 1 / 1.05
)
}
>
<span className="material-icons">text_decrease</span>
</Button>
<Button
className="rounded-l-none rounded-r-none border-x-0"
onClick={() => appLayout.setFontSize(1)}
>
{((appLayout.fontSize ?? 1) * 100).toLocaleString(
undefined,
{
maximumFractionDigits: 0,
}
)}
%
</Button>
<Button
className="rounded-l-none"
onClick={() =>
appLayout.setFontSize(
appLayout.fontSize
? appLayout.fontSize * 1.05
: 1 * 1.05
)
}
>
<span className="material-icons">text_increase</span>
</Button>
</div>
</div>
<div>
<h3 className="text-xl">{langui.font}</h3>
<div className="grid gap-2">
<Button
active={appLayout.dyslexic === false}
onClick={() => appLayout.setDyslexic(false)}
className="font-zenMaruGothic"
>
Zen Maru Gothic
</Button>
<Button
active={appLayout.dyslexic === true}
onClick={() => appLayout.setDyslexic(true)}
className="font-openDyslexic"
>
OpenDyslexic
</Button>
</div>
</div>
<div>
<h3 className="text-xl">{langui.player_name}</h3>
<input
type="text"
placeholder="<player>"
className="w-48"
onInput={(event) =>
appLayout.setPlayerName(
(event.target as HTMLInputElement).value
)
}
/>
</div>
</div>
</div>
</Popup>
</div>
</div>
);
}