Added initial focus trap during exercises/exams

This commit is contained in:
Joao Ramos
2023-08-16 00:08:20 +01:00
parent dd0acbea61
commit 93a5bcf40f
10 changed files with 2940 additions and 3359 deletions

3
.gitignore vendored
View File

@@ -36,4 +36,5 @@ yarn-error.log*
next-env.d.ts next-env.d.ts
.env .env
.yarn/* .yarn/*
.history*

810
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
export default function FocusLayer() {
return <div className="bg-gray-700 bg-opacity-30 absolute top-0 left-0 bottom-0 right-0" />
}

View File

@@ -9,19 +9,20 @@ interface Props {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
navDisabled?: boolean; navDisabled?: boolean;
focusMode?: boolean
} }
export default function Layout({user, children, className, navDisabled = false}: Props) { export default function Layout({user, children, className, navDisabled = false, focusMode = false}: Props) {
const router = useRouter(); const router = useRouter();
return ( return (
<main className="w-full min-h-full h-screen flex flex-col bg-mti-gray-smoke"> <main className="w-full min-h-full h-screen flex flex-col bg-mti-gray-smoke">
<Navbar user={user} navDisabled={navDisabled} /> <Navbar user={user} navDisabled={navDisabled} focusMode={focusMode} />
<div className="h-full w-full flex py-4 pb-8 gap-2"> <div className="h-full w-full flex gap-2">
<Sidebar path={router.pathname} navDisabled={navDisabled} /> <Sidebar path={router.pathname} navDisabled={navDisabled} focusMode={focusMode} />
<div <div
className={clsx( className={clsx(
"w-5/6 min-h-full h-fit mr-8 bg-white shadow-md rounded-2xl p-12 pb-8 flex flex-col gap-12 relative overflow-hidden", "w-5/6 min-h-full h-fit mr-8 bg-white shadow-md rounded-2xl p-12 pb-8 flex flex-col gap-12 relative overflow-hidden mt-2",
className, className,
)}> )}>
{children} {children}

View File

@@ -1,24 +1,31 @@
import {User} from "@/interfaces/user"; import {User} from "@/interfaces/user";
import Link from "next/link"; import Link from "next/link";
import {Avatar} from "primereact/avatar"; import {Avatar} from "primereact/avatar";
import FocusLayer from '@/components/FocusLayer';
import { preventNavigation } from "@/utils/navigation.disabled";
interface Props { interface Props {
user: User; user: User;
navDisabled?: boolean; navDisabled?: boolean;
focusMode?: boolean;
} }
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
export default function Navbar({user, navDisabled = false}: Props) { export default function Navbar({user, navDisabled = false, focusMode = false}: Props) {
const disableNavigation = preventNavigation(navDisabled, focusMode);
return ( return (
<header className="w-full bg-transparent py-4 gap-2 flex items-center"> <header className="w-full bg-transparent py-4 gap-2 flex items-center relative">
<h1 className="font-bold text-2xl w-1/6 px-8">EnCoach</h1> <h1 className="font-bold text-2xl w-1/6 px-8">EnCoach</h1>
<div className="flex justify-between w-5/6 mr-8"> <div className="flex justify-between w-5/6 mr-8">
<input type="text" placeholder="Search..." className="rounded-full py-4 px-6 border border-mti-gray-platinum outline-none" /> <input type="text" placeholder="Search..." className="rounded-full py-4 px-6 border border-mti-gray-platinum outline-none" />
<Link href={!navDisabled ? "/profile" : ""} className="flex gap-3 items-center justify-end"> <Link href={disableNavigation ? "" : "/profile"} className="flex gap-3 items-center justify-end">
<img src={user.profilePicture} alt={user.name} className="w-10 h-10 rounded-full object-cover" /> <img src={user.profilePicture} alt={user.name} className="w-10 h-10 rounded-full object-cover" />
<span className="text-right">{user.name}</span> <span className="text-right">{user.name}</span>
</Link> </Link>
</div> </div>
{focusMode && <FocusLayer/>}
</header> </header>
); );
} }

View File

@@ -8,10 +8,12 @@ import {FaAward} from "react-icons/fa";
import Link from "next/link"; import Link from "next/link";
import {useRouter} from "next/router"; import {useRouter} from "next/router";
import axios from "axios"; import axios from "axios";
import FocusLayer from '@/components/FocusLayer';
import { preventNavigation } from "@/utils/navigation.disabled";
interface Props { interface Props {
path: string; path: string;
navDisabled?: boolean; navDisabled?: boolean;
focusMode?: boolean;
} }
interface NavProps { interface NavProps {
@@ -34,7 +36,7 @@ const Nav = ({Icon, label, path, keyPath, disabled = false}: NavProps) => (
</Link> </Link>
); );
export default function Sidebar({path, navDisabled = false}: Props) { export default function Sidebar({path, navDisabled = false, focusMode = false }: Props) {
const router = useRouter(); const router = useRouter();
const logout = async () => { const logout = async () => {
@@ -43,20 +45,22 @@ export default function Sidebar({path, navDisabled = false}: Props) {
}); });
}; };
const disableNavigation: Boolean = preventNavigation(navDisabled, focusMode);
return ( return (
<section className="h-full flex bg-transparent flex-col justify-between w-1/6 px-4"> <section className="h-full flex bg-transparent flex-col justify-between w-1/6 px-4 py-4 pb-8 relative">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Nav disabled={navDisabled} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" /> <Nav disabled={disableNavigation} Icon={MdSpaceDashboard} label="Dashboard" path={path} keyPath="/" />
<Nav disabled={navDisabled} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" /> <Nav disabled={disableNavigation} Icon={BsFileEarmarkText} label="Exams" path={path} keyPath="/exam" />
<Nav disabled={navDisabled} Icon={BsPencil} label="Exercises" path={path} keyPath="/exercises" /> <Nav disabled={disableNavigation} Icon={BsPencil} label="Exercises" path={path} keyPath="/exercises" />
<Nav disabled={navDisabled} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" /> <Nav disabled={disableNavigation} Icon={BsGraphUp} label="Stats" path={path} keyPath="/stats" />
<Nav disabled={navDisabled} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" /> <Nav disabled={disableNavigation} Icon={BsClockHistory} label="Record" path={path} keyPath="/record" />
</div> </div>
<div <div
role="button" role="button"
tabIndex={1} tabIndex={1}
onClick={logout} onClick={focusMode ? () => {} : logout}
className={clsx( className={clsx(
"p-4 px-8 rounded-full flex gap-4 items-center cursor-pointer text-black hover:text-mti-rose transition duration-300 ease-in-out", "p-4 px-8 rounded-full flex gap-4 items-center cursor-pointer text-black hover:text-mti-rose transition duration-300 ease-in-out",
"absolute bottom-8", "absolute bottom-8",
@@ -64,6 +68,7 @@ export default function Sidebar({path, navDisabled = false}: Props) {
<RiLogoutBoxFill size={20} /> <RiLogoutBoxFill size={20} />
<span className="text-lg font-medium">Log Out</span> <span className="text-lg font-medium">Log Out</span>
</div> </div>
{focusMode && <FocusLayer />}
</section> </section>
); );
} }

View File

@@ -315,7 +315,7 @@ export default function Page() {
</Head> </Head>
<ToastContainer /> <ToastContainer />
{user && ( {user && (
<Layout user={user} className="justify-between"> <Layout user={user} className="justify-between" focusMode={selectedModules.length !== 0}>
{renderScreen()} {renderScreen()}
</Layout> </Layout>
)} )}

View File

@@ -317,7 +317,7 @@ export default function Page() {
</Head> </Head>
<ToastContainer /> <ToastContainer />
{user && ( {user && (
<Layout user={user} className="justify-between"> <Layout user={user} className="justify-between" focusMode={selectedModules.length !== 0}>
{renderScreen()} {renderScreen()}
</Layout> </Layout>
)} )}

View File

@@ -0,0 +1,5 @@
export const preventNavigation = (navDisabled: Boolean, focusMode: Boolean): Boolean => {
if (navDisabled) return true;
if(focusMode) return true;
return false;
}

5427
yarn.lock

File diff suppressed because it is too large Load Diff