-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Menu scaffolding + useAnimationUnmountHook
- Loading branch information
Showing
11 changed files
with
402 additions
and
22 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} />} | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) => {}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters