Skip to content

Commit

Permalink
Menu scaffolding + useAnimationUnmountHook
Browse files Browse the repository at this point in the history
  • Loading branch information
ALPHAAAL committed Mar 17, 2024
1 parent e5375b3 commit 63bf19c
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 22 deletions.
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.1",
"clsx": "^2.1.0",
"localforage": "^1.10.0",
"match-sorter": "^6.3.4",
"react": "^18.2.0",
Expand Down
250 changes: 250 additions & 0 deletions src/assets/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/theme-light-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/About/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default function About() {
<div className="flex flex-col-reverse sm:flex-row w-full">
<div className='sm:w-[60%] sm:pr-3'>
<h1 className="text-7xl font-bold">Alex Lau</h1>
<p className='mt-6 leading-relaxed sm:leading-loose text-lg tracking-wider'>
<div className='mt-6 leading-relaxed sm:leading-loose text-lg tracking-wider'>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur posuere tincidunt nulla in egestas. Etiam in aliquam ipsum, in sagittis ex. Duis fermentum commodo malesuada. Sed consequat magna eu nunc tempus ultrices. Vivamus nisi sem, varius non mi quis, tempor gravida ipsum. Vestibulum sodales, leo et ultrices pellentesque, lorem ipsum pharetra quam, quis dapibus neque felis eu diam. Nunc pellentesque massa eu nibh congue condimentum. Donec eget sem vitae neque dignissim eleifend. Aliquam quis lacus ante. Sed in lectus et dui elementum scelerisque sit amet eu leo. Fusce volutpat dolor et est viverra porttitor.
</p>
Expand All @@ -17,7 +17,7 @@ export default function About() {
<p className='mt-3'>
Mauris malesuada purus tristique pretium convallis. Mauris rutrum leo vel metus malesuada, et ullamcorper libero vulputate. Maecenas pharetra vitae est non hendrerit. Fusce nec nulla enim. Etiam rutrum sem a neque venenatis tincidunt. Vestibulum sed eros purus. Integer cursus dictum ullamcorper. Maecenas eu metus ut nulla ornare tristique sit amet mollis risus.
</p>
</p>
</div>
</div>
<div className='sm:w-[40%]'>
<img src={ProfilePic} />
Expand Down
59 changes: 39 additions & 20 deletions src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,49 @@
import { useState } from 'react';
import { Outlet } from "react-router-dom";
import clsx from 'clsx';

import MyIcon from '../../assets/icon.svg?react';
import DarkMode from '../../assets/theme-light-dark.svg?react';

import NavigationBar from "../NavigationBar";
import NavigationMenu from '../NavigationMenu';
import { AppContext } from '../../contexts/AppContext';

export default function Layout() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const value = {
isMenuOpen,
setIsMenuOpen,
};
const blurClassName = isMenuOpen ? ' blur-[3px]' : '';

return (
<div className="h-max md:px-[10%] lg:px-[15%] bg-slate-800 text-green-300">
<div className="h-full flex flex-col bg-slate-900 px-5">
<div className="flex flex-col min-h-screen py-3">
<div className="mb-5">
Something goes here
<br />
Something goes here
<br />
Something goes here
<AppContext.Provider value={value}>
<div className="h-max md:px-[10%] lg:px-[15%] bg-slate-800 text-green-300">
<div className={clsx("h-full flex flex-col bg-slate-900 px-5", blurClassName)}>
<div className="flex flex-col min-h-screen py-3">
<div className="flex justify-between mb-5">
<MyIcon className='text-white cursor-pointer' width={48} height={48} />
<div className='flex'>
<DarkMode className='text-white cursor-pointer' width={48} height={48} />
<NavigationMenu />
</div>
</div>
<div className="grow">
<Outlet />
</div>
</div>
<div className="grow">
<Outlet />
<div className="pb-3 border-t border-green-300" />
<div className="flex sm:px-5 sm:justify-between justify-around">
<h1 className="hidden sm:block sm:w-[40%] text-center">Wisdom has been chasing me, but I have alawys been faster.</h1>
<h1 className="sm:w-[40%] text-center">
Built with React & Tailwind CSS <br />
2024 Alex Lau. All rights reserved.
</h1>
</div>
</div>
<div className="pb-3 border-t border-green-300" />
<div className="flex sm:px-5 sm:justify-between justify-around">
<h1 className="hidden sm:block sm:w-[50%] text-center">The greatest glory in living lies not in never falling, but in rising every time we fall.</h1>
<h1 className="sm:w-[50%] text-center">
Built with React & Tailwind CSS <br />
2024 Alex Lau. All rights reserved.
</h1>
</div>
<NavigationBar />
</div>
</div>
</AppContext.Provider>
)
}
11 changes: 11 additions & 0 deletions src/components/NavigationBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Link } from "react-router-dom";

export default function NavigationBar() {
return (
<div className={`px-2 sm:flex sm:justify-between hidden fixed bottom-5 left-[50%] ml-[-150px] bg-red-400 w-[300px] h-[50px]`}>
<Link to="/about">About</Link>
<Link to="/about">Gallery</Link>
<Link to="/about">Blog</Link>
</div>
)
}
44 changes: 44 additions & 0 deletions src/components/NavigationMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useContext } from "react"
import { createPortal } from "react-dom";
import clsx from "clsx";

import { AppContext } from "../../contexts/AppContext";
import { useAnimationEndUnmmount } from "../../hooks/useAnimationEndUnmount";

function Menu({ onMenuClose, isMenuOpen } : {
onMenuClose: () => void,
isMenuOpen: boolean,
}) {
const shouldRender = useAnimationEndUnmmount('menu', isMenuOpen);

// TODO: Check why enter-effect cannot use transition, but have to rely on `animate-open-menu`
return shouldRender && createPortal(
(
<div id='menu' className={clsx(isMenuOpen ? '' : 'scale-y-0', "animate-open-menu transition-all duration-[150ms] flex flex-col justify-around fixed border-2 rounded-2xl w-[90%] top-4 left-[5%] bg-slate-300 p-3")}>
<div className="flex justify-between">
<p>Navigation</p>
<p onClick={() => onMenuClose()}>X</p>
</div>
<ul>
<li>Menu Item 1</li>
<li>Menu Item 2</li>
<li>Menu Item 3</li>
</ul>
</div>
),
document.body,
);
}

export default function NavigationMenu() {
const { isMenuOpen, setIsMenuOpen } = useContext(AppContext);

return (
<>
<div className='sm:hidden w-[75px] border-2 rounded-2xl border-slate-800' onClick={() => setIsMenuOpen(true)}>
Menu
</div>
{<Menu onMenuClose={() => setIsMenuOpen(false)} isMenuOpen={isMenuOpen} />}
</>
)
}
7 changes: 7 additions & 0 deletions src/contexts/AppContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createContext } from "react";

export const AppContext = createContext({
isMenuOpen: false,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
setIsMenuOpen: (_: boolean) => {},
});
29 changes: 29 additions & 0 deletions src/hooks/useAnimationEndUnmount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";

export function useAnimationEndUnmmount(id: string, isMounted: boolean) {
const [shouldRender, setShouldRender] = useState(false);

useEffect(() => {
const element = document.getElementById(id);

if (isMounted && !shouldRender) {
setShouldRender(true);
} else if (!isMounted && shouldRender) {
if (!element) {
throw new Error('useAnimationEndUnmmount cannot find element id');
}

const handleAnimationEnd = () => {
setShouldRender(false);
};

element.addEventListener('transitionend', handleAnimationEnd);

return () => {
element.removeEventListener('transitionend', handleAnimationEnd)
};
}
}, [id, isMounted, shouldRender]);

return shouldRender;
}
9 changes: 9 additions & 0 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ export default {
theme: {
extend: {
keyframes: {
open: {
"0%": {
transform: 'scaleY(0)',
},
"100%": {
transform: 'scaleY(1)',
}
},
typing: {
"0%": {
width: "0%",
Expand Down Expand Up @@ -45,6 +53,7 @@ export default {
},
},
animation: {
'open-menu': 'open 0.25s',
typewriter: "typing 2s steps(18), cursor .7s infinite",
'text-slide-4': 'typing 2s linear alternate infinite, text-slide-4 16s steps(1) infinite', // 16s for text-slide-4 because 2s * 2 (alternate animation)
}
Expand Down

0 comments on commit 63bf19c

Please sign in to comment.