νμλΆμ΄ λ§λ€μ΄μ£Όμ μ¬μ΄λ λ©λ΄ μ»΄ν¬λνΈλ₯Ό μ¬μ©ν΄μ νμ΄μ§ 쑰립νλλ° μλ‘κ³ μΉ¨ μ μ¬μ΄λ λ©λ΄κ° λμλ€κ° λ€μ΄κ°λ μ΄μλ₯Ό λ°κ²¬ν μν©, κΈ°μ‘΄μ λ§λ€μ΄μ£Όμ ¨μ λλ μ΄μκ° μμμ§λ§ λ°°κ²½μμ΄ ν¬λͺ μΌλ‘ λμ΄μμ΄ μΈμ§ νμ§ λͺ»ν μν©

μ²μμλ μ¬μ΄λ λ©λ΄κ° isOpen μνλ‘ μ‘°κ±΄λΆ λ λλ§μ μ€μ mount, unmount νλ λ°©μμΈ μ€ μμμ§λ§, css μμ±μΌλ‘ transform: translateX(-100%) λ·°ν¬νΈμλ§ λ³΄μ΄μ§ μκ² νλ λ°©μμ΄μλ€.
function SideMenu({ dashboards }: SideMenuProps) {
const [isOpen, setIsOpen] = useAtom(sideMenuAtom);
const [renderDelayed, setRenderDelayed] = useState(false);
const sideMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleSideMenu = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (target.dataset.state === 'sideMenuToggle') {
setIsOpen((prev) => !prev);
return;
}
if (sideMenuRef.current && !sideMenuRef.current.contains(target)) {
setIsOpen(false);
return;
}
};
document.addEventListener('click', handleSideMenu);
const timeout = setTimeout(() => {
setRenderDelayed(true);
}, 400);
return () => {
document.removeEventListener('click', handleSideMenu);
clearTimeout(timeout);
};
}, [setIsOpen]);
// λλ¨Έμ§ μ»΄ν¬λνΈ μ½λ...
}
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideOut {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
.sideMenu {
width: 18.75rem;
height: calc(100vh - 4.3rem);
background-color: $color-white-ffffff;
position: fixed;
top: 4.3rem;
left: 0;
animation: slideOut 0.4s ease forwards;
z-index: 999;
@include mobile {
width: 16rem;
top: 3.75rem;
height: calc(100vh - 3.75rem);
}
}
.open {
animation: slideIn 0.4s ease forwards;
}
μ½λλ₯Ό μ΄ν΄λ³΄λ©΄ μ΄κΈ° ν΄λμ€ λ€μμ animation: slideOut 0.4s ease forwardsμ΄ μ€μ λμ΄ μμ΄ μλ‘κ³ μΉ¨νλ©΄ 첫 λ λλ§ μμ transform: translateX(0)μ΄μλ€κ° transform: translateX(-100%) μΌλ‘ λ³νκΈ° λλ¬Έμ μ¬μ΄λ λ©λ΄κ° 보μλ€κ° μ¬λΌμ§λ νμμ΄ μκΈ°κ³ μλ€!
첫 λ λλ§μμ slideOut μ λλ©μ΄μ
μλν΄μ λ·°ν¬νΈμ 보μλ€κ° μ¬λΌμ§λ κ²μ΄ λ¬Έμ μ΄κΈ° λλ¬Έμ κΈ°λ³Έ μ€νμΌμ transform: translateX(-100%)λ₯Ό μ§μ νκ³ μ²« λ λλ§μ΄ μλ£λμλ€λλΌλ κ²μ μ μ μλ stateλ₯Ό νλ μμ±ν΄ 첫 λ λλ§μ΄ μλ£λλ©΄ κ·Έ μ΄νμ slideOut μ λλ©μ΄μ
μ μ§μ νμ¬ λ΄κ° μνλ λμμ μμΌμ€λ€.
function SideMenu({ dashboards }: SideMenuProps) {
const [isOpen, setIsOpen] = useAtom(sideMenuAtom);
const [isFirstRender, setIsFirstRender] = useState(false);
useEffect(() => {
const handleSideMenu = (e: MouseEvent) => {
const target = e.target as HTMLElement;
if (target.dataset.state === 'sideMenuToggle') {
setIsOpen((prev) => !prev);
return;
}
if (sideMenuRef.current && !sideMenuRef.current.contains(target)) {
setIsOpen(false);
return;
}
};
// 첫 λ λλ§ μμλ slideOut μ λλ©μ΄μ
μλ x
setIsFirstRender(true);
// 첫 λ λλ§μ΄ λλκ³ isOpenμ΄ falseμΈ κ²½μ°μ slideOut μ λλ©μ΄μ
μ λΆμ¬νλ€.
if (isFirstRender && !isOpen) {
sideMenuRef.current?.classList.add(styles.close);
}
document.addEventListener('click', handleSideMenu);
const timeout = setTimeout(() => {
setRenderDelayed(true);
}, 400);
return () => {
document.removeEventListener('click', handleSideMenu);
clearTimeout(timeout);
};
}, [isOpen]);
// λλ¨Έμ§ μ»΄ν¬λνΈ μ½λ../
}
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideOut {
from {
transform: translateX(0%);
}
to {
transform: translateX(-100%);
}
}
.sideMenu {
width: 18.75rem;
height: calc(100vh - 4.3rem);
background-color: $color-white-ffffff;
position: absolute;
top: 4.3rem;
left: 0;
transform: translateX(-100%); // κΈ°λ³Έμ μΌλ‘ λ·°ν¬νΈμ μ보μ΄κ² μ€μ
z-index: 999;
@include mobile {
width: 16rem;
top: 3.75rem;
height: calc(100vh - 3.75rem);
}
}
.close { // 첫 λ λλ§μ΄ λκ³ , isOpenμ΄ falseμΈ κ²½μ°μ μ λλ©μ΄μ
λΆμ¬
animation: slideOut 0.4s ease forwards;
}
.open {
animation: slideIn 0.4s ease forwards;
}
μ΄μ μλ‘κ³ μΉ¨ νλλΌλ μ²μμλ μ λλ©μ΄μ μ΄ μλ μνλ‘ μ¬μ΄λ λ©λ΄κ° λ·°ν¬νΈμ 보μ΄μ§ μμ μ΄μκ° ν΄κ²°λμλ€!
