Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
20 changes: 20 additions & 0 deletions components/Headers/Alarm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
interface AlarmProps {
isLoggedIn: boolean;
}

/**
* 알림 컴포넌트
* @param isLoggedIn 로그인 여부 판별
*/

export default function Alarm({ isLoggedIn }: AlarmProps) {
if (!isLoggedIn) {
return null;
}

return (
<button>
<img src="/icon/icon-alarm.svg" className="mo:hidden" alt="알림 아이콘" />
</button>
);
}
14 changes: 14 additions & 0 deletions components/Headers/GNB.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Link from 'next/link';

export default function GNB() {
return (
<div className="flex items-center gap-10">
<Link className="mo:hidden" href="/wikilist">
위키목록
</Link>
<Link className="mo:hidden" href="/boards">
자유게시판
</Link>
</div>
);
}
50 changes: 50 additions & 0 deletions components/Headers/Headers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Link from 'next/link';
import { useEffect, useState } from 'react';

import Alarm from './Alarm';
import GNB from './GNB';
import Login from './Login';

export default function Headers() {
// TODO 임시 로그인 상태(추후 업데이트예정)
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [isMobile, setIsMobile] = useState(false);
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 (
<header 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">
<div className="flex gap-10">
<Link href="/">
<img src="/logo.svg" alt="위키드 로고" />
</Link>
<GNB />
</div>
{/* 로그인 여부에 따라 조건부로 노출 */}
<div className="flex items-center gap-5">
<Alarm isLoggedIn={isLoggedIn} />
<Login
login={handleLogin}
logout={handleLogout}
isMobile={isMobile}
isLoggedIn={isLoggedIn}
/>
</div>
</header>
);
}
114 changes: 114 additions & 0 deletions components/Headers/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import useOutsideClick from 'hooks/useOutsideClick';
import { useRouter } from 'next/router';
import { useEffect, useRef, useState } from 'react';

import Menu from '../Menu';

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

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

export default function Login({
login,
logout,
isMobile,
isLoggedIn,
}: LoginProps) {
const [isOpen, setIsOpen] = useState(false);
const [profileMenu, setProfileMenu] = useState<string[]>([]);

//TODO 프로필 이미지 처리(추후 업데이트)
const profileImage = null;
const router = useRouter();
const loginMenuRef = useRef<HTMLDivElement>(null);

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

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

const handleLoginMenu = (option: string) => {
if (option === '위키목록') {
router.push('/wikilist');
} else if (option === '자유게시판') {
router.push('/boards');
} else if (option === '마이페이지') {
router.push('/mypage');
}
//TODO 로그인 처리 (추후 업데이트)
else if (option === '로그인') {
login();
} else if (option === '로그아웃') {
logout();
}
};

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

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

{isOpen && (
<Menu
options={profileMenu}
onSelect={handleLoginMenu}
menuSize="w-28"
/>
)}
</button>
</div>
) : isMobile ? (
<div ref={loginMenuRef} className="flex">
<button className="relative" onClick={() => setIsOpen(!isOpen)}>
<img
src="/icon/icon-menu.svg"
className="hidden mo:block"
alt="메뉴 아이콘"
/>
{isOpen && (
<Menu
options={profileMenu}
onSelect={handleLoginMenu}
menuSize="w-28"
/>
)}
</button>
</div>
) : (
<div className="mo:hidden" onClick={login}>
로그인
</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/Headers/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Headers from '@/components/Headers/Headers';

export default function HeaderTest() {
return (
<div>
<Headers />
</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