Added foundation for a minimizable navbar
This commit is contained in:
parent
290fa80b31
commit
bcb9e5fd7d
|
@ -6,6 +6,7 @@ import { useSwipeable } from "react-swipeable";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Button from "components/Button";
|
import Button from "components/Button";
|
||||||
import { prettyLanguage } from "queries/helpers";
|
import { prettyLanguage } from "queries/helpers";
|
||||||
|
import { useMediaDesktop, useMediaMobile } from "hooks/useMediaQuery";
|
||||||
|
|
||||||
type AppLayoutProps = {
|
type AppLayoutProps = {
|
||||||
subPanel?: React.ReactNode;
|
subPanel?: React.ReactNode;
|
||||||
|
@ -22,6 +23,9 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const [mainPanelOpen, setMainPanelOpen] = useState(false);
|
const [mainPanelOpen, setMainPanelOpen] = useState(false);
|
||||||
const [subPanelOpen, setSubPanelOpen] = useState(false);
|
const [subPanelOpen, setSubPanelOpen] = useState(false);
|
||||||
const [languagePanelOpen, setLanguagePanelOpen] = useState(false);
|
const [languagePanelOpen, setLanguagePanelOpen] = useState(false);
|
||||||
|
const [mainPanelReduced, setMainPanelReduced] = useState(false);
|
||||||
|
const isMobile = useMediaMobile();
|
||||||
|
const isDesktop = useMediaDesktop();
|
||||||
const sensibilitySwipe = 1.1;
|
const sensibilitySwipe = 1.1;
|
||||||
|
|
||||||
const handlers = useSwipeable({
|
const handlers = useSwipeable({
|
||||||
|
@ -43,18 +47,22 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mainPanelClass =
|
const mainPanelClass = `fixed desktop:left-0 desktop:top-0 desktop:bottom-0 transition-all ${
|
||||||
"fixed desktop:left-0 desktop:top-0 desktop:bottom-0 desktop:w-[20rem]";
|
mainPanelReduced ? "desktop:w-[8rem]" : "desktop:w-[20rem]"
|
||||||
const subPanelClass =
|
}`;
|
||||||
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:w-[20rem]";
|
const subPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:w-[20rem] transition-all ${
|
||||||
|
mainPanelReduced ? " desktop:left-[8rem]" : "desktop:left-[20rem]"
|
||||||
|
}`;
|
||||||
let contentPanelClass = "";
|
let contentPanelClass = "";
|
||||||
let turnSubIntoContent = false;
|
let turnSubIntoContent = false;
|
||||||
if (props.subPanel && props.contentPanel) {
|
if (props.subPanel && props.contentPanel) {
|
||||||
contentPanelClass =
|
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 transition-all ${
|
||||||
"fixed desktop:left-[40rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
|
mainPanelReduced ? "desktop:left-[28rem]" : "desktop:left-[40rem]"
|
||||||
|
}`;
|
||||||
} else if (props.contentPanel) {
|
} else if (props.contentPanel) {
|
||||||
contentPanelClass =
|
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 transition-all ${
|
||||||
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
|
mainPanelReduced ? "desktop:left-[8rem]" : "desktop:left-[20rem]"
|
||||||
|
}`;
|
||||||
} else if (props.subPanel) {
|
} else if (props.subPanel) {
|
||||||
turnSubIntoContent = true;
|
turnSubIntoContent = true;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +122,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
className={`fixed bg-dark inset-0 transition-opacity duration-500
|
className={`fixed bg-dark inset-0 transition-opacity duration-500
|
||||||
${turnSubIntoContent ? "z-10" : ""}
|
${turnSubIntoContent ? "z-10" : ""}
|
||||||
${
|
${
|
||||||
mainPanelOpen || subPanelOpen
|
(mainPanelOpen || subPanelOpen) && isMobile
|
||||||
? "opacity-50"
|
? "opacity-50"
|
||||||
: "opacity-0 pointer-events-none touch-none"
|
: "opacity-0 pointer-events-none touch-none"
|
||||||
}`}
|
}`}
|
||||||
|
@ -150,9 +158,22 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
<MainPanel
|
<MainPanel
|
||||||
langui={props.langui}
|
langui={props.langui}
|
||||||
setLanguagePanelOpen={setLanguagePanelOpen}
|
setLanguagePanelOpen={setLanguagePanelOpen}
|
||||||
|
reduced={mainPanelReduced && isDesktop}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Main panel minimize button*/}
|
||||||
|
<div
|
||||||
|
className={`mobile:hidden translate-x-0 fixed top-1/2 z-20 ${
|
||||||
|
mainPanelReduced ? "left-[6.65rem]" : "left-[18.65rem]"
|
||||||
|
}`}
|
||||||
|
onClick={() => setMainPanelReduced(!mainPanelReduced)}
|
||||||
|
>
|
||||||
|
<Button className="material-icons bg-light !px-2">
|
||||||
|
{mainPanelReduced ? "chevron_right" : "chevron_left"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Language selection background */}
|
{/* Language selection background */}
|
||||||
<div
|
<div
|
||||||
className={`fixed bg-dark inset-0 transition-all duration-500 z-20 grid place-content-center ${
|
className={`fixed bg-dark inset-0 transition-all duration-500 z-20 grid place-content-center ${
|
||||||
|
|
|
@ -7,6 +7,7 @@ type NavOptionProps = {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
border?: boolean;
|
border?: boolean;
|
||||||
|
reduced?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function NavOption(props: NavOptionProps): JSX.Element {
|
export default function NavOption(props: NavOptionProps): JSX.Element {
|
||||||
|
@ -20,15 +21,25 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
|
||||||
} ${isActive ? divActive : ""}`;
|
} ${isActive ? divActive : ""}`;
|
||||||
|
|
||||||
if (props.icon) {
|
if (props.icon) {
|
||||||
return (
|
if (props.reduced) {
|
||||||
<Link href={props.url} passHref>
|
return (
|
||||||
<div className={`grid grid-cols-[auto_1fr] text-left ${divCommon}`}>
|
<Link href={props.url} passHref>
|
||||||
<span className="material-icons mt-[.1em]">{props.icon}</span>
|
<div className={`grid ${divCommon}`}>
|
||||||
<h3 className="text-2xl">{props.title}</h3>
|
<span className="place-self-center material-icons mt-[.1em]">{props.icon}</span>
|
||||||
{props.subtitle && <p className="col-start-2">{props.subtitle}</p>}
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</Link>
|
);
|
||||||
);
|
} else {
|
||||||
|
return (
|
||||||
|
<Link href={props.url} passHref>
|
||||||
|
<div className={`grid grid-cols-[auto_1fr] text-left ${divCommon}`}>
|
||||||
|
<span className="material-icons mt-[.1em]">{props.icon}</span>
|
||||||
|
<h3 className="text-2xl">{props.title}</h3>
|
||||||
|
{props.subtitle && <p className="col-start-2">{props.subtitle}</p>}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Link href={props.url} passHref>
|
<Link href={props.url} passHref>
|
||||||
|
|
|
@ -10,38 +10,64 @@ import Markdown from "markdown-to-jsx";
|
||||||
type MainPanelProps = {
|
type MainPanelProps = {
|
||||||
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
|
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
|
||||||
setLanguagePanelOpen: Function;
|
setLanguagePanelOpen: Function;
|
||||||
|
reduced: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function MainPanel(props: MainPanelProps): JSX.Element {
|
export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
const langui = props.langui;
|
const langui = props.langui;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const reduced = props.reduced;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="mainPanel"
|
id="mainPanel"
|
||||||
className="flex flex-col justify-center content-start p-8 gap-y-2 justify-items-center text-center"
|
className="flex flex-col justify-center content-start p-8 gap-y-2 justify-items-center text-center"
|
||||||
>
|
>
|
||||||
<div className="">
|
{reduced ? (
|
||||||
<div className="grid place-items-center">
|
<div className="grid place-items-center gap-4">
|
||||||
<Link href="/" passHref>
|
<Link href="/" passHref>
|
||||||
<div className="w-1/2 cursor-pointer transition-[filter] hover:colorize-dark">
|
<div className="w-8 cursor-pointer transition-[filter] hover:colorize-dark">
|
||||||
<SVG
|
<SVG
|
||||||
src={"/icons/accords.svg"}
|
src={"/icons/accords.svg"}
|
||||||
alt={"Logo of Accord's Library"}
|
alt={"Logo of Accord's Library"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="relative mt-5" onClick={() => props.setLanguagePanelOpen(true)}>
|
{router.locale ? (
|
||||||
{router.locale ? (
|
<div onClick={() => props.setLanguagePanelOpen(true)}>
|
||||||
<Button className="absolute right-0 top-[-1.3em] text-xs !py-0.5 !px-2.5">
|
<Button className="text-xs">{router.locale.toUpperCase()}</Button>
|
||||||
{router.locale.toUpperCase()}
|
</div>
|
||||||
</Button>
|
) : (
|
||||||
) : (
|
""
|
||||||
""
|
)}
|
||||||
)}
|
</div>
|
||||||
<h2 className="text-3xl">Accord’s Library</h2>
|
) : (
|
||||||
|
<div>
|
||||||
|
<div className="grid place-items-center">
|
||||||
|
<Link href="/" passHref>
|
||||||
|
<div className="w-1/2 cursor-pointer transition-[filter] hover:colorize-dark">
|
||||||
|
<SVG
|
||||||
|
src={"/icons/accords.svg"}
|
||||||
|
alt={"Logo of Accord's Library"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<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()}
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
|
<h2 className="text-3xl">Accord’s Library</h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
|
@ -50,6 +76,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
icon="library_books"
|
icon="library_books"
|
||||||
title={langui.main_library}
|
title={langui.main_library}
|
||||||
subtitle={langui.main_library_description}
|
subtitle={langui.main_library_description}
|
||||||
|
reduced={reduced}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
|
@ -57,6 +84,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
icon="travel_explore"
|
icon="travel_explore"
|
||||||
title={langui.main_wiki}
|
title={langui.main_wiki}
|
||||||
subtitle={langui.main_wiki_description}
|
subtitle={langui.main_wiki_description}
|
||||||
|
reduced={reduced}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
|
@ -64,27 +92,49 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||||
icon="watch_later"
|
icon="watch_later"
|
||||||
title={langui.main_chronicles}
|
title={langui.main_chronicles}
|
||||||
subtitle={langui.main_chronicles_description}
|
subtitle={langui.main_chronicles_description}
|
||||||
|
reduced={reduced}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<NavOption url="/news" icon="feed" title={langui.main_news} />
|
<NavOption
|
||||||
<NavOption url="/merch" icon="store" title={langui.main_merch} />
|
url="/news"
|
||||||
|
icon="feed"
|
||||||
|
title={langui.main_news}
|
||||||
|
reduced={reduced}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NavOption
|
||||||
|
url="/merch"
|
||||||
|
icon="store"
|
||||||
|
title={langui.main_merch}
|
||||||
|
reduced={reduced}
|
||||||
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/gallery"
|
url="/gallery"
|
||||||
icon="collections"
|
icon="collections"
|
||||||
title={langui.main_gallery}
|
title={langui.main_gallery}
|
||||||
|
reduced={reduced}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/archives"
|
url="/archives"
|
||||||
icon="inventory"
|
icon="inventory"
|
||||||
title={langui.main_archives}
|
title={langui.main_archives}
|
||||||
|
reduced={reduced}
|
||||||
/>
|
/>
|
||||||
<NavOption url="/about-us" icon="info" title={langui.main_about_us} />
|
|
||||||
|
|
||||||
<HorizontalLine />
|
<NavOption
|
||||||
|
url="/about-us"
|
||||||
|
icon="info"
|
||||||
|
title={langui.main_about_us}
|
||||||
|
reduced={reduced}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="text-center">
|
{reduced ? "" : <HorizontalLine />}
|
||||||
|
|
||||||
|
<div className={`text-center ${reduced ? "hidden" : ""}`}>
|
||||||
<p>
|
<p>
|
||||||
{langui.main_licensing ? (
|
{langui.main_licensing ? (
|
||||||
<Markdown>{langui.main_licensing}</Markdown>
|
<Markdown>{langui.main_licensing}</Markdown>
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function useMediaQuery(query: string): boolean {
|
||||||
|
const getMatches = (query: string): boolean => {
|
||||||
|
// Prevents SSR issues
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
return window.matchMedia(query).matches;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [matches, setMatches] = useState<boolean>(getMatches(query));
|
||||||
|
|
||||||
|
function handleChange() {
|
||||||
|
setMatches(getMatches(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const matchMedia = window.matchMedia(query);
|
||||||
|
|
||||||
|
// Triggered at the first client-side load and if query changes
|
||||||
|
handleChange();
|
||||||
|
|
||||||
|
// Listen matchMedia
|
||||||
|
matchMedia.addEventListener("change", handleChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
matchMedia.removeEventListener("change", handleChange);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMediaThin() {
|
||||||
|
return useMediaQuery("(max-width: 50ch)");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMediaMobile() {
|
||||||
|
return useMediaQuery("(max-width: 150ch)");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMediaDesktop() {
|
||||||
|
return useMediaQuery("(min-width: 150ch)");
|
||||||
|
}
|
||||||
|
|
|
@ -56,6 +56,18 @@
|
||||||
|
|
||||||
.prose {
|
.prose {
|
||||||
--tw-prose-bullets: theme("colors.dark") !important;
|
--tw-prose-bullets: theme("colors.dark") !important;
|
||||||
|
--tw-prose-quote-borders: theme("colors.dark") !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose footer {
|
||||||
|
@apply border-t-[3px] border-dotted pt-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose footer > div {
|
||||||
|
@apply my-2 px-6 py-4 rounded-xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose footer > div:target {
|
||||||
|
@apply bg-mid shadow-inner-sm shadow-dark;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue