-
Notifications
You must be signed in to change notification settings - Fork 2
✨ feat: 사이드바 컴포넌트 구현 #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| 'use client' | ||
|
|
||
| import Image from 'next/image' | ||
|
|
||
| import { CreateDashboardButtonProps } from '@/app/shared/types/dashboard' | ||
|
|
||
| export default function CreateDashboardButton({ | ||
| onClick, | ||
| }: CreateDashboardButtonProps): JSX.Element { | ||
| return ( | ||
| <button | ||
| type="button" | ||
| onClick={onClick} | ||
| className="flex size-20 items-center justify-center rounded-6 transition-colors hover:bg-gray-50" | ||
| aria-label="새 대시보드 생성" | ||
| > | ||
| <div className="relative size-16"> | ||
| <Image | ||
| src="/images/invitation.png" | ||
| alt="대시보드 생성" | ||
| fill | ||
| className="object-contain" | ||
| /> | ||
| </div> | ||
| </button> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| 'use client' | ||
|
|
||
| import Image from 'next/image' | ||
|
|
||
| import { DashboardItemProps } from '@/app/shared/types/dashboard' | ||
|
|
||
| export default function DashboardItem({ | ||
| dashboard, | ||
| isActive = false, | ||
| onClick, | ||
| }: DashboardItemProps): JSX.Element { | ||
| const handleClick = () => { | ||
| onClick(dashboard.id) | ||
| } | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| aria-current={isActive ? 'page' : undefined} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 처음에는 ARIA없이 구현을 했었는데, 따로 AI 코드리뷰 과정에서 스크린 리더 사용자들은 시각적 표현을 인식할 수 없어서 현재 위치를 알기 어렵다는 피드백을 주더라구요. 그래서 따로 웹 접근성을 고려해 한번 추가해봤습니다. 물론 기능적으로는 없어도 동작하지만요. 필수는 아니지만 그냥 습관을 기른다고 생각하는 차원에서 반영해봤어요. 리뷰 덕분에 WAI-ARIA 관련해서 이런것들이 있구나 하고 찾아보게 되었네요 :) |
||
| onClick={handleClick} | ||
| className={`Text-black flex w-full items-center gap-12 rounded-6 px-12 py-8 text-left text-18 transition-colors hover:bg-gray-50 ${ | ||
| isActive ? 'BG-currentDashboard font-medium' : '' | ||
| }`} | ||
| > | ||
| {/* 컬러 도트 */} | ||
| <div | ||
| className="size-8 flex-shrink-0 rounded-full" | ||
| style={{ backgroundColor: dashboard.color }} | ||
| /> | ||
|
|
||
| {/* 대시보드 제목 */} | ||
| <span className="flex-1 truncate">{dashboard.title}</span> | ||
|
|
||
| {/* 내가 만든 대시보드에 왕관 아이콘 */} | ||
| {dashboard.createdByMe && ( | ||
| <div className="relative h-12 w-14 flex-shrink-0"> | ||
| <Image | ||
| src="/images/crown.png" | ||
| alt="내가 만든 대시보드" | ||
| fill | ||
| className="object-contain" | ||
| /> | ||
| </div> | ||
| )} | ||
| </button> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| 'use client' | ||
|
|
||
| import Image from 'next/image' | ||
| import Link from 'next/link' | ||
| import { usePathname, useRouter } from 'next/navigation' | ||
|
|
||
| import CreateDashboardButton from './CreateDashboardButton' | ||
| import DashboardItem from './DashboardItem' | ||
|
|
||
| export default function Sidebar(): JSX.Element { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| const pathname = usePathname() | ||
| const router = useRouter() | ||
|
|
||
| // TODO: 목데이터 - API 연동시 삭제예정 | ||
| const mockDashboards = [ | ||
| { | ||
| id: 1, | ||
| title: '비브러리', | ||
| color: '#10B981', | ||
| createdByMe: true, | ||
| createdAt: '', | ||
| updatedAt: '', | ||
| userId: 1, | ||
| }, | ||
| { | ||
| id: 2, | ||
| title: '코드잇', | ||
| color: '#8B5CF6', | ||
| createdByMe: true, | ||
| createdAt: '', | ||
| updatedAt: '', | ||
| userId: 1, | ||
| }, | ||
| { | ||
| id: 3, | ||
| title: '3분기 계획', | ||
| color: '#F59E0B', | ||
| createdByMe: false, | ||
| createdAt: '', | ||
| updatedAt: '', | ||
| userId: 2, | ||
| }, | ||
| { | ||
| id: 4, | ||
| title: '회의록', | ||
| color: '#3B82F6', | ||
| createdByMe: false, | ||
| createdAt: '', | ||
| updatedAt: '', | ||
| userId: 3, | ||
| }, | ||
| { | ||
| id: 5, | ||
| title: '중요 문서함', | ||
| color: '#EC4899', | ||
| createdByMe: false, | ||
| createdAt: '', | ||
| updatedAt: '', | ||
| userId: 4, | ||
| }, | ||
| ] | ||
|
|
||
| const handleDashboardClick = (dashboardId: number) => { | ||
| router.push(`/dashboard/${dashboardId}`) | ||
| } | ||
|
|
||
| const handleCreateDashboard = () => { | ||
| // TODO: 대시보드 생성 모달 열기 | ||
| console.log('대시보드 생성 모달 열기임') | ||
| } | ||
| return ( | ||
| <aside className="BG-white Border-section fixed left-0 top-0 h-screen w-300 overflow-y-auto"> | ||
| {/* 로고 섹션 */} | ||
| <div className="flex h-70 items-center px-20"> | ||
| <Link href="/" className="flex items-center gap-8"> | ||
| <div className="relative h-35 w-150"> | ||
| <Image | ||
| src="/images/logo-light2.svg" | ||
| alt="Coplan logo" | ||
| fill | ||
| className="object-contain" | ||
| priority | ||
| /> | ||
| </div> | ||
| </Link> | ||
| </div> | ||
|
|
||
| {/* 대시보드 섹션 */} | ||
| <div className="px-20 py-24"> | ||
| {/* 섹션 헤더 */} | ||
| <div className="mb-24 flex items-center justify-between"> | ||
| <h2 className="Text-gray text-12 font-semibold">Dash Boards</h2> | ||
| <CreateDashboardButton onClick={handleCreateDashboard} /> | ||
| </div> | ||
|
|
||
| {/* 대시보드 목록 */} | ||
| <div className="space-y-8"> | ||
| {mockDashboards.map((dashboard) => ( | ||
| <DashboardItem | ||
| key={dashboard.id} | ||
| dashboard={dashboard} | ||
| isActive={pathname === `/dashboard/${dashboard.id}`} | ||
| onClick={handleDashboardClick} | ||
| /> | ||
| ))} | ||
| </div> | ||
| </div> | ||
|
Comment on lines
+89
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분은 추후에 시멘틱까지 고려한다면
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 감사합니다 :) 추후에 여유가 생기면 시멘틱까지 고려해서 수정해봐야겠네요.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍👍 |
||
| </aside> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // 사이드바용 대시보드 타입 | ||
| export interface Dashboard { | ||
| id: number | ||
| title: string | ||
| color: string | ||
| createdAt: string | ||
| updatedAt: string | ||
| createdByMe: boolean | ||
| userId: number | ||
| } | ||
|
|
||
| // 대시보드 목록 조회 응답 | ||
| export interface DashboardListResponse { | ||
| cursorId: number | ||
| totalCount: number | ||
| dashboards: Dashboard[] | ||
| } | ||
|
|
||
| // 사이드바 컴포넌트 Props | ||
| export interface DashboardItemProps { | ||
| dashboard: Dashboard | ||
| isActive?: boolean | ||
| onClick: (dashboardId: number) => void | ||
| } | ||
|
|
||
| export interface CreateDashboardButtonProps { | ||
| onClick: () => void | ||
| } | ||
|
|
||
| // 대시보드 생성 요청 타입 | ||
| export interface CreateDashboardRequest { | ||
| title: string | ||
| color: string | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
background 색상의 경우 global 스타일로 빼서 사용하면 좋지 않을까 합니다!!
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감사합니다 :) 중복으로 사용할 일이 생기면 global 스타일로 빼서 올려두겠습니다 👍