Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 4 additions & 17 deletions components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import useOutsideClick from 'hooks/useOutsideClick';
import { useRef, useState } from 'react';

import Menu from './Menu';

Expand Down Expand Up @@ -32,24 +33,10 @@ export default function Dropdown({
};

//외부 클릭시 드롭다운 닫기
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
useOutsideClick(dropdownRef, () => setIsOpen(false));

return (
<div ref={dropdownRef}>
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsOpen(!isOpen)}
className={`${dropdownSize} flex items-center justify-between rounded-xl border border-gray-300 bg-background px-5 py-3.5 text-14 leading-none text-gray-400 hover:border-green-200 focus:ring-1 focus:ring-green-200`}
Expand Down
7 changes: 7 additions & 0 deletions components/GNB/Alarm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Alarm() {
return (
<button>
<img src="/icon/icon-alarm.svg" className="mo:hidden" alt="알림 아이콘" />
</button>
);
}
48 changes: 48 additions & 0 deletions components/GNB/GNB.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useEffect, useRef, useState } from 'react';

import Alarm from './Alarm';
import Header from './Header';
import Login from './Login';
import ProfileIcon from './ProfileIcon';

export default function GNB() {
const [isLoggedIn, setIsLoggedIn] = useState(false); // ANCHOR TODO
const [isMobile, setIsMobile] = useState(false);
const profileMenuRef = useRef<HTMLDivElement>(null);

const handleLogin = () => {
setIsLoggedIn(true);
};

const handleLogout = () => {
setIsLoggedIn(false);
};

useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth <= 767);
checkMobile();
window.addEventListener('resize', checkMobile);

return () => {
window.removeEventListener('resize', checkMobile);
};
}, []);

return (
<div
ref={profileMenuRef}
className="z-100 fixed left-0 top-0 flex h-20 w-full items-center justify-between bg-background px-20 py-5 shadow-custom"
>
<Header />

{isLoggedIn ? (
<div className="flex items-center gap-5">
<Alarm />
<ProfileIcon isMobile={isMobile} logout={handleLogout} />
</div>
) : (
<Login login={handleLogin} isMobile={isMobile} />
)}
</div>
);
}
19 changes: 19 additions & 0 deletions components/GNB/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useRouter } from 'next/router';

export default function Header() {
const router = useRouter();

return (
<div className="flex items-center gap-10">
<button onClick={() => router.push('/')}>
<img src="/logo.svg" alt="위키드 로고" />
</button>
<button className="mo:hidden" onClick={() => router.push('/wikilist')}>
위키목록
</button>
<button className="mo:hidden" onClick={() => router.push('/boards')}>
자유게시판
</button>
</div>
);
}
52 changes: 52 additions & 0 deletions components/GNB/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import useOutsideClick from 'hooks/useOutsideClick';
import { useRouter } from 'next/router';
import { useRef, useState } from 'react';

import Menu from '../Menu';

interface LoginProps {
login: () => void;
isMobile: boolean;
}

export default function Login({ login, isMobile }: LoginProps) {
const [isOpen, setIsOpen] = useState(false);
const mobileMenu = ['로그인', '위키목록', '자유게시판'];
const router = useRouter();
const loginMenuRef = useRef<HTMLDivElement>(null);

const handleLoginMenu = (option: string) => {
if (option === '위키목록') {
router.push('/wikilist');
} else if (option === '자유게시판') {
router.push('/boards');
} else if (option === '로그인') {
login();
}
};

useOutsideClick(loginMenuRef, () => setIsOpen(false));

return isMobile ? (
<div ref={loginMenuRef}>
<button
onClick={() => setIsOpen(!isOpen)}
className="relative hidden mo:block"
>
<img src="/icon/icon-menu.svg" alt="메뉴 아이콘" />

{isOpen && (
<Menu
options={mobileMenu}
onSelect={handleLoginMenu}
menuSize="w-28"
/>
)}
</button>
</div>
) : (
<button onClick={login} className="mo:hidden">
로그인
</button>
);
}
75 changes: 75 additions & 0 deletions components/GNB/ProfileIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import useOutsideClick from 'hooks/useOutsideClick';
import { useRouter } from 'next/router';
import { useEffect, useRef, useState } from 'react';

import Menu from '../Menu';

interface ProfileIconProps {
logout: () => void;
isMobile: boolean;
}

/**
* 프로필아이콘 컴포넌트
* @param logout 로그아웃처리를 위한 함수
* @param isMobile 화면 크기에 따라 모바일 여부 판별
*/

export default function ProfileIcon({ isMobile, logout }: ProfileIconProps) {
const profileImage = null; //ANCHOR
const [profileMenu, setProfileMenu] = useState<string[]>([]);
const [isOpen, setIsOpen] = useState(false);
const router = useRouter();
const profileMenuRef = useRef<HTMLDivElement>(null);

const updateProfileMenu = () => {
if (!isMobile) {
return ['마이페이지', '로그아웃'];
} else if (isMobile) {
return ['위키목록', '자유게시판', '알림', '마이페이지', '로그아웃'];
} else return [];
};

useEffect(() => {
setProfileMenu(updateProfileMenu());
}, [isMobile]);

const handleProfileMenu = (option: string) => {
if (option === '위키목록') {
router.push('/wikilist');
} else if (option === '자유게시판') {
router.push('/boards');
} else if (option === '마이페이지') {
router.push('/mypage');
} else if (option === '로그아웃') {
logout();
}
};

useOutsideClick(profileMenuRef, () => setIsOpen(false));

return (
<div ref={profileMenuRef} className="flex items-center">
<button className="relative" onClick={() => setIsOpen(!isOpen)}>
<img
src={profileImage || '/icon/icon-profile.svg'} //ANCHOR
className="mo:hidden"
alt="프로필 아이콘"
/>
<img
src="/icon/icon-menu.svg"
className="hidden mo:block"
alt="메뉴 아이콘"
/>

{isOpen && (
<Menu
options={profileMenu}
onSelect={handleProfileMenu}
menuSize="w-28"
/>
)}
</button>
</div>
);
}
2 changes: 1 addition & 1 deletion components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface MenuProps {
export default function Menu({ options, onSelect, menuSize }: MenuProps) {
return (
<ul
className={`${menuSize} absolute z-10 mt-2 rounded-xl border border-gray-300 bg-background p-1 text-14 shadow-custom`}
className={`${menuSize} absolute right-1/2 z-10 mt-2 translate-x-1/2 rounded-xl border border-gray-300 bg-background p-1 text-14 shadow-custom`}
>
{options.map((option, index) => (
<li
Expand Down
20 changes: 20 additions & 0 deletions hooks/useOutsideClick.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useEffect } from 'react';

const useOutsideClick = (
ref: React.RefObject<HTMLElement | null>,
callback: () => void
) => {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
callback();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref, callback]);
};

export default useOutsideClick;
9 changes: 9 additions & 0 deletions pages/test/GNB/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import GNB from '@/components/GNB/GNB';

export default function GNBtest() {
return (
<div>
<GNB />
</div>
);
}
10 changes: 4 additions & 6 deletions pages/test/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useState } from 'react';

import Button from '@/components/Button';
import Dropdown from '@/components/Dropdown';
import LinkBar from '@/components/LinkBar';
Expand Down Expand Up @@ -61,9 +63,7 @@ export default function Test() {
<tbody>
<tr>
<td className={commonCellClass}>inputfield</td>
<td className={commonRowClass}>
<Signup />
</td>
<td className={commonRowClass}>{/* <Signup /> */}</td>
</tr>
<tr className="border-b border-gray-300">
<td className={commonCellClass}>Button</td>
Expand Down Expand Up @@ -98,9 +98,7 @@ export default function Test() {
<LinkBar link="https://www.google.com" />

<td className={commonCellClass}>inputfield</td>
<td className={commonRowClass}>
<Signup />
</td>
<td className={commonRowClass}>{/* <Signup /> */}</td>
</td>
</tr>
<tr className="border-b border-gray-300">
Expand Down
4 changes: 4 additions & 0 deletions public/icon/icon-alarm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icon/icon-menu.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: 4 additions & 0 deletions public/icon/icon-profile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading