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 Button from "components/Button";
|
||||
import { prettyLanguage } from "queries/helpers";
|
||||
import { useMediaDesktop, useMediaMobile } from "hooks/useMediaQuery";
|
||||
|
||||
type AppLayoutProps = {
|
||||
subPanel?: React.ReactNode;
|
||||
|
@ -22,6 +23,9 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
const [mainPanelOpen, setMainPanelOpen] = useState(false);
|
||||
const [subPanelOpen, setSubPanelOpen] = useState(false);
|
||||
const [languagePanelOpen, setLanguagePanelOpen] = useState(false);
|
||||
const [mainPanelReduced, setMainPanelReduced] = useState(false);
|
||||
const isMobile = useMediaMobile();
|
||||
const isDesktop = useMediaDesktop();
|
||||
const sensibilitySwipe = 1.1;
|
||||
|
||||
const handlers = useSwipeable({
|
||||
|
@ -43,18 +47,22 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
},
|
||||
});
|
||||
|
||||
const mainPanelClass =
|
||||
"fixed desktop:left-0 desktop:top-0 desktop:bottom-0 desktop:w-[20rem]";
|
||||
const subPanelClass =
|
||||
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:w-[20rem]";
|
||||
const mainPanelClass = `fixed desktop:left-0 desktop:top-0 desktop:bottom-0 transition-all ${
|
||||
mainPanelReduced ? "desktop:w-[8rem]" : "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 turnSubIntoContent = false;
|
||||
if (props.subPanel && props.contentPanel) {
|
||||
contentPanelClass =
|
||||
"fixed desktop:left-[40rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
|
||||
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 transition-all ${
|
||||
mainPanelReduced ? "desktop:left-[28rem]" : "desktop:left-[40rem]"
|
||||
}`;
|
||||
} else if (props.contentPanel) {
|
||||
contentPanelClass =
|
||||
"fixed desktop:left-[20rem] desktop:top-0 desktop:bottom-0 desktop:right-0";
|
||||
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 transition-all ${
|
||||
mainPanelReduced ? "desktop:left-[8rem]" : "desktop:left-[20rem]"
|
||||
}`;
|
||||
} else if (props.subPanel) {
|
||||
turnSubIntoContent = true;
|
||||
}
|
||||
|
@ -114,7 +122,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
className={`fixed bg-dark inset-0 transition-opacity duration-500
|
||||
${turnSubIntoContent ? "z-10" : ""}
|
||||
${
|
||||
mainPanelOpen || subPanelOpen
|
||||
(mainPanelOpen || subPanelOpen) && isMobile
|
||||
? "opacity-50"
|
||||
: "opacity-0 pointer-events-none touch-none"
|
||||
}`}
|
||||
|
@ -150,9 +158,22 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
<MainPanel
|
||||
langui={props.langui}
|
||||
setLanguagePanelOpen={setLanguagePanelOpen}
|
||||
reduced={mainPanelReduced && isDesktop}
|
||||
/>
|
||||
</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 */}
|
||||
<div
|
||||
className={`fixed bg-dark inset-0 transition-all duration-500 z-20 grid place-content-center ${
|
||||
|
|
|
@ -7,6 +7,7 @@ type NavOptionProps = {
|
|||
title: string;
|
||||
subtitle?: string;
|
||||
border?: boolean;
|
||||
reduced?: boolean;
|
||||
};
|
||||
|
||||
export default function NavOption(props: NavOptionProps): JSX.Element {
|
||||
|
@ -20,15 +21,25 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
|
|||
} ${isActive ? divActive : ""}`;
|
||||
|
||||
if (props.icon) {
|
||||
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>
|
||||
);
|
||||
if (props.reduced) {
|
||||
return (
|
||||
<Link href={props.url} passHref>
|
||||
<div className={`grid ${divCommon}`}>
|
||||
<span className="place-self-center material-icons mt-[.1em]">{props.icon}</span>
|
||||
</div>
|
||||
</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 {
|
||||
return (
|
||||
<Link href={props.url} passHref>
|
||||
|
|
|
@ -10,38 +10,64 @@ import Markdown from "markdown-to-jsx";
|
|||
type MainPanelProps = {
|
||||
langui: GetWebsiteInterfaceQuery["websiteInterfaces"]["data"][number]["attributes"];
|
||||
setLanguagePanelOpen: Function;
|
||||
reduced: boolean;
|
||||
};
|
||||
|
||||
export default function MainPanel(props: MainPanelProps): JSX.Element {
|
||||
const langui = props.langui;
|
||||
const router = useRouter();
|
||||
const reduced = props.reduced;
|
||||
|
||||
return (
|
||||
<div
|
||||
id="mainPanel"
|
||||
className="flex flex-col justify-center content-start p-8 gap-y-2 justify-items-center text-center"
|
||||
>
|
||||
<div className="">
|
||||
<div className="grid place-items-center">
|
||||
{reduced ? (
|
||||
<div className="grid place-items-center gap-4">
|
||||
<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
|
||||
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>
|
||||
{router.locale ? (
|
||||
<div onClick={() => props.setLanguagePanelOpen(true)}>
|
||||
<Button className="text-xs">{router.locale.toUpperCase()}</Button>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<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>
|
||||
)}
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
|
@ -50,6 +76,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
|||
icon="library_books"
|
||||
title={langui.main_library}
|
||||
subtitle={langui.main_library_description}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
|
@ -57,6 +84,7 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
|||
icon="travel_explore"
|
||||
title={langui.main_wiki}
|
||||
subtitle={langui.main_wiki_description}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
|
@ -64,27 +92,49 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
|||
icon="watch_later"
|
||||
title={langui.main_chronicles}
|
||||
subtitle={langui.main_chronicles_description}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOption url="/news" icon="feed" title={langui.main_news} />
|
||||
<NavOption url="/merch" icon="store" title={langui.main_merch} />
|
||||
<NavOption
|
||||
url="/news"
|
||||
icon="feed"
|
||||
title={langui.main_news}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
url="/merch"
|
||||
icon="store"
|
||||
title={langui.main_merch}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
url="/gallery"
|
||||
icon="collections"
|
||||
title={langui.main_gallery}
|
||||
reduced={reduced}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
url="/archives"
|
||||
icon="inventory"
|
||||
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>
|
||||
{langui.main_licensing ? (
|
||||
<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 {
|
||||
--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