Changed Applayout from fixed to grid + using custom tooltip
This commit is contained in:
parent
f4217a597c
commit
4b30dac878
|
@ -54,27 +54,6 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
},
|
||||
});
|
||||
|
||||
const mainPanelClass = `fixed desktop:left-0 desktop:top-0 desktop:bottom-0 ${
|
||||
appLayout.mainPanelReduced ? "desktop:w-[6rem]" : "desktop:w-[20rem]"
|
||||
}`;
|
||||
const subPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:w-[20rem] ${
|
||||
appLayout.mainPanelReduced ? " desktop:left-[6rem]" : "desktop:left-[20rem]"
|
||||
}`;
|
||||
let contentPanelClass = "";
|
||||
if (subPanel) {
|
||||
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${
|
||||
appLayout.mainPanelReduced
|
||||
? "desktop:left-[26rem]"
|
||||
: "desktop:left-[40rem]"
|
||||
}`;
|
||||
} else if (contentPanel) {
|
||||
contentPanelClass = `fixed desktop:top-0 desktop:bottom-0 desktop:right-0 ${
|
||||
appLayout.mainPanelReduced
|
||||
? "desktop:left-[6rem]"
|
||||
: "desktop:left-[20rem]"
|
||||
}`;
|
||||
}
|
||||
|
||||
const turnSubIntoContent = subPanel && !contentPanel;
|
||||
|
||||
const titlePrefix = "Accord’s Library";
|
||||
|
@ -115,6 +94,21 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
// 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
|
||||
className={`${
|
||||
|
@ -127,7 +121,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
>
|
||||
<div
|
||||
{...handlers}
|
||||
className="fixed inset-0 touch-pan-y p-0 m-0 bg-light text-black"
|
||||
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>
|
||||
|
@ -157,9 +151,32 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
<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={`top-0 left-0 right-0 bottom-20 overflow-y-scroll bg-light texture-paper-dots ${contentPanelClass}`}
|
||||
className={`[grid-area:content] overflow-y-scroll bg-light texture-paper-dots`}
|
||||
>
|
||||
{contentPanel ? (
|
||||
contentPanel
|
||||
|
@ -173,38 +190,15 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Background when navbar is opened */}
|
||||
<div
|
||||
className={`fixed inset-0 transition-[backdrop-filter] duration-500 ${
|
||||
(appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile
|
||||
? "[backdrop-filter:blur(2px)]"
|
||||
: "pointer-events-none touch-none "
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`fixed bg-shade inset-0 transition-opacity duration-500
|
||||
${turnSubIntoContent ? "z-10" : ""}
|
||||
${
|
||||
(appLayout.mainPanelOpen || appLayout.subPanelOpen) && isMobile
|
||||
? "opacity-60"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
onClick={() => {
|
||||
appLayout.setMainPanelOpen(false);
|
||||
appLayout.setSubPanelOpen(false);
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
{/* Sub panel */}
|
||||
{subPanel && (
|
||||
<div
|
||||
className={`${subPanelClass} border-r-[1px] mobile:bottom-20 mobile:border-r-0 mobile:border-l-[1px] border-black border-dotted top-0 bottom-0 right-0 left-12 overflow-y-scroll webkit-scrollbar:w-0 [scrollbar-width:none] transition-transform duration-300 bg-light texture-paper-dots
|
||||
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:translate-x-0 mobile:left-0 mobile:border-l-0"
|
||||
? "mobile:border-l-0 mobile:w-full"
|
||||
: !appLayout.subPanelOpen
|
||||
? "mobile:translate-x-full"
|
||||
? "mobile:translate-x-[100vw]"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
|
@ -214,28 +208,14 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||
|
||||
{/* Main panel */}
|
||||
<div
|
||||
className={`${mainPanelClass} border-r-[1px] mobile:bottom-20 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 texture-paper-dots
|
||||
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>
|
||||
|
||||
{/* Main panel minimize button*/}
|
||||
<div
|
||||
className={`mobile:hidden translate-x-0 fixed top-1/2 z-20 ${
|
||||
appLayout.mainPanelReduced ? "left-[4.65rem]" : "left-[18.65rem]"
|
||||
}`}
|
||||
onClick={() =>
|
||||
appLayout.setMainPanelReduced(!appLayout.mainPanelReduced)
|
||||
}
|
||||
>
|
||||
<Button className="material-icons bg-light !px-2">
|
||||
{appLayout.mainPanelReduced ? "chevron_right" : "chevron_left"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Navbar */}
|
||||
<div className="fixed inset-0 z-30 top-auto h-20 border-t-[1px] border-black border-dotted grid grid-cols-[5rem_1fr_5rem] place-items-center desktop:hidden bg-light texture-paper-dots">
|
||||
<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={() => {
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import { MouseEventHandler } from "react";
|
||||
|
||||
type ChipProps = {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
onMouseEnter?: MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
||||
};
|
||||
|
||||
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 transition-[color,_opacity,border-color] ${props.className} `}
|
||||
className={`grid place-content-center place-items-center text-xs pb-[0.14rem] px-1.5 border-[1px] rounded-full opacity-70 transition-[color,_opacity,_border-color] hover:opacity-100 ${props.className} `}
|
||||
onMouseEnter={props.onMouseEnter}
|
||||
onMouseLeave={props.onMouseLeave}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
|
|
|
@ -55,7 +55,7 @@ export default function LibraryItemsPreview(
|
|||
<h3 className="mobile:text-xs leading-3">{item.subtitle}</h3>
|
||||
</div>
|
||||
|
||||
<div className="w-full grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:w-0 [scrollbar-width:none] place-content-start">
|
||||
<div className="w-full grid grid-flow-col gap-1 overflow-x-scroll webkit-scrollbar:h-0 [scrollbar-width:none] place-content-start">
|
||||
{item.categories.data.map((category) => (
|
||||
<Chip key={category.id} className="text-sm">
|
||||
{category.attributes.short}
|
||||
|
|
|
@ -22,10 +22,18 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element {
|
|||
slugify: slugify,
|
||||
overrides: {
|
||||
h2: {
|
||||
component: (props: { id: string; children: React.ReactNode }) => {
|
||||
component: (props: {
|
||||
id: string;
|
||||
style: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-row place-items-center place-content-center gap-3 hover:[--anchor-opacity:100] [--anchor-opacity:0]">
|
||||
<h2 id={props.id}>{props.children}</h2>
|
||||
<h2
|
||||
id={props.id}
|
||||
className="flex flex-row place-items-center place-content-center gap-3 hover:[--anchor-opacity:100] [--anchor-opacity:0]"
|
||||
style={props.style}
|
||||
>
|
||||
{props.children}
|
||||
<abbr title="Copy anchor link">
|
||||
<span
|
||||
className="material-icons opacity-[var(--anchor-opacity)] transition-all hover:text-dark cursor-pointer"
|
||||
|
@ -41,7 +49,7 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element {
|
|||
link
|
||||
</span>
|
||||
</abbr>
|
||||
</div>
|
||||
</h2>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import { MouseEventHandler } from "react";
|
||||
import { MouseEventHandler, useState } from "react";
|
||||
import ToolTip from "components/ToolTip";
|
||||
|
||||
type NavOptionProps = {
|
||||
url: string;
|
||||
|
@ -22,11 +23,15 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
|
|||
props.border ? border : ""
|
||||
} ${isActive ? divActive : ""}`;
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<Link href={props.url} passHref>
|
||||
<div
|
||||
onClick={props.onClick}
|
||||
className={`grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${
|
||||
onMouseEnter={() => props.reduced && setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
className={`relative grid grid-flow-col grid-cols-[auto] auto-cols-fr justify-center ${
|
||||
props.icon ? "text-left" : "text-center"
|
||||
} ${divCommon}`}
|
||||
>
|
||||
|
@ -40,6 +45,11 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
|
|||
{props.subtitle && <p className="col-start-2">{props.subtitle}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ToolTip hovered={hovered} direction="right" offset="3.5rem">
|
||||
<h3 className="text-2xl">{props.title}</h3>
|
||||
{props.subtitle && <p className="col-start-2">{props.subtitle}</p>}
|
||||
</ToolTip>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
|
|
@ -24,6 +24,20 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
|
|||
appLayout.mainPanelReduced && isDesktop && "px-4"
|
||||
}`}
|
||||
>
|
||||
{/* Reduce/expand main menu */}
|
||||
<div
|
||||
className={`mobile:hidden top-1/2 fixed ${
|
||||
appLayout.mainPanelReduced ? "left-[4.65rem]" : "left-[18.65rem]"
|
||||
}`}
|
||||
onClick={() =>
|
||||
appLayout.setMainPanelReduced(!appLayout.mainPanelReduced)
|
||||
}
|
||||
>
|
||||
<Button className="material-icons bg-light !px-2">
|
||||
{appLayout.mainPanelReduced ? "chevron_right" : "chevron_left"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="grid place-items-center">
|
||||
<Link href="/" passHref>
|
||||
|
|
|
@ -4,7 +4,7 @@ type SubPanelProps = {
|
|||
|
||||
export default function SubPanel(props: SubPanelProps): JSX.Element {
|
||||
return (
|
||||
<div className="flex flex-col p-8 gap-y-2 justify-items-center text-center mobile:h-full">
|
||||
<div className="grid pt-10 pb-20 px-6 desktop:py-8 desktop:px-10 gap-y-2 justify-items-center text-center">
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,6 +3,9 @@ import {
|
|||
GetContentTextQuery,
|
||||
GetWebsiteInterfaceQuery,
|
||||
} from "graphql/operations-types";
|
||||
import { useState } from "react";
|
||||
import Img, { ImageQuality } from "./Img";
|
||||
import ToolTip from "./ToolTip";
|
||||
|
||||
type RecorderChipProps = {
|
||||
className?: string;
|
||||
|
@ -13,11 +16,59 @@ type RecorderChipProps = {
|
|||
export default function RecorderChip(props: RecorderChipProps): JSX.Element {
|
||||
const recorder = props.recorder;
|
||||
const langui = props.langui;
|
||||
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<Chip key={recorder.id}>
|
||||
<Chip
|
||||
key={recorder.id}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
>
|
||||
{recorder.attributes.anonymize
|
||||
? `Recorder#${recorder.attributes.anonymous_code}`
|
||||
: recorder.attributes.username}
|
||||
|
||||
<ToolTip
|
||||
hovered={hovered}
|
||||
direction="top"
|
||||
offset="1.5rem"
|
||||
delayShow={150}
|
||||
>
|
||||
<div className="p-2 py-5 grid gap-2">
|
||||
<div className="grid grid-flow-col gap-2 place-items-center place-content-start">
|
||||
{recorder.attributes.avatar.data && (
|
||||
<Img
|
||||
className="w-8 rounded-full"
|
||||
image={recorder.attributes.avatar.data.attributes}
|
||||
quality={ImageQuality.Small}
|
||||
rawImg
|
||||
/>
|
||||
)}
|
||||
<h3 className="text-xl">{recorder.attributes.username}</h3>
|
||||
</div>
|
||||
{recorder.attributes.languages.data.length > 0 && (
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
<p>{langui.languages}:</p>
|
||||
{recorder.attributes.languages.data.map((language) => (
|
||||
<Chip key={language.attributes.code}>
|
||||
{language.attributes.code.toUpperCase()}
|
||||
</Chip>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{recorder.attributes.pronouns && (
|
||||
<div className="flex flex-row flex-wrap gap-1">
|
||||
<p>{langui.pronouns}:</p>
|
||||
<Chip>{recorder.attributes.pronouns}</Chip>
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
{recorder.attributes.bio.length > 0 &&
|
||||
recorder.attributes.bio[0].bio}
|
||||
</p>
|
||||
</div>
|
||||
</ToolTip>
|
||||
</Chip>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
type ToolTipProps = {
|
||||
hovered: boolean;
|
||||
children: React.ReactNode;
|
||||
delayShow?: number;
|
||||
direction: "right" | "bottom" | "top" | "left";
|
||||
offset: string;
|
||||
};
|
||||
|
||||
export default function ToolTip(props: ToolTipProps): JSX.Element {
|
||||
const { children, hovered, direction, offset } = props;
|
||||
let { delayShow } = props;
|
||||
if (delayShow === undefined) delayShow = 500;
|
||||
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let timeout = setTimeout(() => {});
|
||||
if (hovered) {
|
||||
timeout = setTimeout(() => hovered && setShow(true), delayShow);
|
||||
} else {
|
||||
setShow(false);
|
||||
}
|
||||
return () => clearTimeout(timeout);
|
||||
}, [delayShow, hovered]);
|
||||
|
||||
let tooltipCSS = "";
|
||||
let transformCSS = "";
|
||||
let arrowParentCSS = "";
|
||||
let arrowCSS = "";
|
||||
|
||||
switch (direction) {
|
||||
case "left":
|
||||
tooltipCSS = "[justify-self:end] [align-self:center]";
|
||||
transformCSS = `translateX(-${offset})`;
|
||||
arrowParentCSS = "w-4 -right-4 top-0 bottom-0";
|
||||
arrowCSS = "border-l-light";
|
||||
break;
|
||||
|
||||
case "right":
|
||||
tooltipCSS = "[justify-self:start] [align-self:center]";
|
||||
transformCSS = `translateX(${offset})`;
|
||||
arrowParentCSS = "w-4 -left-4 top-0 bottom-0";
|
||||
arrowCSS = "border-r-light";
|
||||
break;
|
||||
|
||||
case "top":
|
||||
tooltipCSS = "[align-self:end]";
|
||||
transformCSS = `translateY(-${offset})`;
|
||||
arrowParentCSS = "h-4 -bottom-4 left-0 right-0";
|
||||
arrowCSS = "border-t-light";
|
||||
break;
|
||||
|
||||
case "bottom":
|
||||
tooltipCSS = "[align-self:start]";
|
||||
transformCSS = `translateY(${offset})`;
|
||||
arrowParentCSS = "h-4 -top-4 left-0 right-0";
|
||||
arrowCSS = "border-b-light";
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed z-[100] drop-shadow-shade-xl transition-opacity max-w-sm ${
|
||||
show
|
||||
? "opacity-100 pointer-events-auto"
|
||||
: "opacity-0 pointer-events-none"
|
||||
} ${tooltipCSS}`}
|
||||
style={{ transform: transformCSS }}
|
||||
>
|
||||
<div className={`absolute grid ${arrowParentCSS}`}>
|
||||
<div
|
||||
className={`w-0 h-0 border-8 border-[transparent] place-self-center ${arrowCSS}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="p-2 px-4 bg-light rounded-md">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -15,6 +15,11 @@ export default function Home(props: HomeProps): JSX.Element {
|
|||
const { post } = props;
|
||||
const contentPanel = (
|
||||
<ContentPanel>
|
||||
<div className="grid place-items-center place-content-center w-full gap-5 text-center">
|
||||
<div className="[mask:url('/icons/accords.svg')] [mask-size:contain] [mask-repeat:no-repeat] [mask-position:center] w-32 aspect-square mobile:w-[50vw] bg-black" />
|
||||
<h1 className="text-5xl mb-0">Accord’s Library</h1>
|
||||
<h2 className="text-xl -mt-5">Discover • Analyse • Translate • Archive</h2>
|
||||
</div>
|
||||
{post.translations.length > 0 && (
|
||||
<Markdawn text={post.translations[0].body} />
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue