Changed Applayout from fixed to grid + using custom tooltip

This commit is contained in:
DrMint 2022-03-13 02:27:34 +01:00
parent f4217a597c
commit 4b30dac878
10 changed files with 229 additions and 75 deletions

View File

@ -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 turnSubIntoContent = subPanel && !contentPanel;
const titlePrefix = "Accords Library"; const titlePrefix = "Accords Library";
@ -115,6 +94,21 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [currencySelect]); }, [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 ( return (
<div <div
className={`${ className={`${
@ -127,7 +121,7 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
> >
<div <div
{...handlers} {...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> <Head>
<title>{`${titlePrefix} - ${ogTitle}`}</title> <title>{`${titlePrefix} - ${ogTitle}`}</title>
@ -157,9 +151,32 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
<meta name="twitter:image" content={metaImage.image}></meta> <meta name="twitter:image" content={metaImage.image}></meta>
</Head> </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 */} {/* Content panel */}
<div <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 ? (
contentPanel contentPanel
@ -173,38 +190,15 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
)} )}
</div> </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 */} {/* Sub panel */}
{subPanel && ( {subPanel && (
<div <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 turnSubIntoContent
? "mobile:translate-x-0 mobile:left-0 mobile:border-l-0" ? "mobile:border-l-0 mobile:w-full"
: !appLayout.subPanelOpen : !appLayout.subPanelOpen
? "mobile:translate-x-full" ? "mobile:translate-x-[100vw]"
: "" : ""
}`} }`}
> >
@ -214,28 +208,14 @@ export default function AppLayout(props: AppLayoutProps): JSX.Element {
{/* Main panel */} {/* Main panel */}
<div <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"}`} ${appLayout.mainPanelOpen ? "" : "mobile:-translate-x-full"}`}
> >
<MainPanel langui={langui} /> <MainPanel langui={langui} />
</div> </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 */} {/* 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 <span
className="material-icons mt-[.1em] cursor-pointer" className="material-icons mt-[.1em] cursor-pointer"
onClick={() => { onClick={() => {

View File

@ -1,12 +1,18 @@
import { MouseEventHandler } from "react";
type ChipProps = { type ChipProps = {
className?: string; className?: string;
children: React.ReactNode; children: React.ReactNode;
onMouseEnter?: MouseEventHandler<HTMLDivElement>;
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
}; };
export default function Chip(props: ChipProps): JSX.Element { export default function Chip(props: ChipProps): JSX.Element {
return ( return (
<div <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} {props.children}
</div> </div>

View File

@ -55,7 +55,7 @@ export default function LibraryItemsPreview(
<h3 className="mobile:text-xs leading-3">{item.subtitle}</h3> <h3 className="mobile:text-xs leading-3">{item.subtitle}</h3>
</div> </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) => ( {item.categories.data.map((category) => (
<Chip key={category.id} className="text-sm"> <Chip key={category.id} className="text-sm">
{category.attributes.short} {category.attributes.short}

View File

@ -22,10 +22,18 @@ export default function Markdawn(props: ScenBreakProps): JSX.Element {
slugify: slugify, slugify: slugify,
overrides: { overrides: {
h2: { h2: {
component: (props: { id: string; children: React.ReactNode }) => { component: (props: {
id: string;
style: React.CSSProperties;
children: React.ReactNode;
}) => {
return ( return (
<div className="flex flex-row place-items-center place-content-center gap-3 hover:[--anchor-opacity:100] [--anchor-opacity:0]"> <h2
<h2 id={props.id}>{props.children}</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"> <abbr title="Copy anchor link">
<span <span
className="material-icons opacity-[var(--anchor-opacity)] transition-all hover:text-dark cursor-pointer" 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 link
</span> </span>
</abbr> </abbr>
</div> </h2>
); );
}, },
}, },

View File

@ -1,6 +1,7 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import Link from "next/link"; import Link from "next/link";
import { MouseEventHandler } from "react"; import { MouseEventHandler, useState } from "react";
import ToolTip from "components/ToolTip";
type NavOptionProps = { type NavOptionProps = {
url: string; url: string;
@ -22,11 +23,15 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
props.border ? border : "" props.border ? border : ""
} ${isActive ? divActive : ""}`; } ${isActive ? divActive : ""}`;
const [hovered, setHovered] = useState(false);
return ( return (
<Link href={props.url} passHref> <Link href={props.url} passHref>
<div <div
onClick={props.onClick} 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" props.icon ? "text-left" : "text-center"
} ${divCommon}`} } ${divCommon}`}
> >
@ -40,6 +45,11 @@ export default function NavOption(props: NavOptionProps): JSX.Element {
{props.subtitle && <p className="col-start-2">{props.subtitle}</p>} {props.subtitle && <p className="col-start-2">{props.subtitle}</p>}
</div> </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> </div>
</Link> </Link>
); );

View File

@ -24,6 +24,20 @@ export default function MainPanel(props: MainPanelProps): JSX.Element {
appLayout.mainPanelReduced && isDesktop && "px-4" 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>
<div className="grid place-items-center"> <div className="grid place-items-center">
<Link href="/" passHref> <Link href="/" passHref>

View File

@ -4,7 +4,7 @@ type SubPanelProps = {
export default function SubPanel(props: SubPanelProps): JSX.Element { export default function SubPanel(props: SubPanelProps): JSX.Element {
return ( 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} {props.children}
</div> </div>
); );

View File

@ -3,6 +3,9 @@ import {
GetContentTextQuery, GetContentTextQuery,
GetWebsiteInterfaceQuery, GetWebsiteInterfaceQuery,
} from "graphql/operations-types"; } from "graphql/operations-types";
import { useState } from "react";
import Img, { ImageQuality } from "./Img";
import ToolTip from "./ToolTip";
type RecorderChipProps = { type RecorderChipProps = {
className?: string; className?: string;
@ -13,11 +16,59 @@ type RecorderChipProps = {
export default function RecorderChip(props: RecorderChipProps): JSX.Element { export default function RecorderChip(props: RecorderChipProps): JSX.Element {
const recorder = props.recorder; const recorder = props.recorder;
const langui = props.langui; const langui = props.langui;
const [hovered, setHovered] = useState(false);
return ( return (
<Chip key={recorder.id}> <Chip
key={recorder.id}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{recorder.attributes.anonymize {recorder.attributes.anonymize
? `Recorder#${recorder.attributes.anonymous_code}` ? `Recorder#${recorder.attributes.anonymous_code}`
: recorder.attributes.username} : 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> </Chip>
); );
} }

View File

@ -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>
);
}

View File

@ -15,6 +15,11 @@ export default function Home(props: HomeProps): JSX.Element {
const { post } = props; const { post } = props;
const contentPanel = ( const contentPanel = (
<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&rsquo;s Library</h1>
<h2 className="text-xl -mt-5">Discover Analyse Translate Archive</h2>
</div>
{post.translations.length > 0 && ( {post.translations.length > 0 && (
<Markdawn text={post.translations[0].body} /> <Markdawn text={post.translations[0].body} />
)} )}