From 9ce52919639e3eb638d621e4bd205e95804e0d7d Mon Sep 17 00:00:00 2001 From: Lseojeong Date: Thu, 6 Nov 2025 17:35:45 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=94=20=EA=B5=AC=ED=98=84=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/icons/ic_analysis.svg | 4 +- src/assets/icons/ic_calculate.svg | 8 +- src/assets/icons/ic_calendar.svg | 8 +- src/assets/icons/ic_chat.svg | 14 +++ src/assets/icons/ic_my.svg | 4 +- src/components/Icons/index.ts | 25 +++++ src/components/Navigation/index.stories.tsx | 65 +++++++++++ src/components/Navigation/index.tsx | 113 ++++++++++++++++++++ 8 files changed, 229 insertions(+), 12 deletions(-) create mode 100644 src/assets/icons/ic_chat.svg create mode 100644 src/components/Icons/index.ts create mode 100644 src/components/Navigation/index.stories.tsx create mode 100644 src/components/Navigation/index.tsx diff --git a/src/assets/icons/ic_analysis.svg b/src/assets/icons/ic_analysis.svg index c192043..2ef1d69 100644 --- a/src/assets/icons/ic_analysis.svg +++ b/src/assets/icons/ic_analysis.svg @@ -1,7 +1,7 @@ - - + + diff --git a/src/assets/icons/ic_calculate.svg b/src/assets/icons/ic_calculate.svg index 8f870f9..77c358f 100644 --- a/src/assets/icons/ic_calculate.svg +++ b/src/assets/icons/ic_calculate.svg @@ -1,9 +1,9 @@ - - - - + + + + diff --git a/src/assets/icons/ic_calendar.svg b/src/assets/icons/ic_calendar.svg index 59bfce9..6d6e376 100644 --- a/src/assets/icons/ic_calendar.svg +++ b/src/assets/icons/ic_calendar.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/src/assets/icons/ic_chat.svg b/src/assets/icons/ic_chat.svg new file mode 100644 index 0000000..42cf0a9 --- /dev/null +++ b/src/assets/icons/ic_chat.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/assets/icons/ic_my.svg b/src/assets/icons/ic_my.svg index d3f6aa7..9c8b5ad 100644 --- a/src/assets/icons/ic_my.svg +++ b/src/assets/icons/ic_my.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/components/Icons/index.ts b/src/components/Icons/index.ts new file mode 100644 index 0000000..bcbf148 --- /dev/null +++ b/src/components/Icons/index.ts @@ -0,0 +1,25 @@ +// Navigation Icons +export { default as CalendarIcon } from '@/assets/icons/ic_calendar.svg'; +export { default as AnalysisIcon } from '@/assets/icons/ic_analysis.svg'; +export { default as ChatIcon } from '@/assets/icons/ic_chat.svg'; +export { default as CalculateIcon } from '@/assets/icons/ic_calculate.svg'; +export { default as MyIcon } from '@/assets/icons/ic_my.svg'; + +// Action Icons +export { default as CheckIcon } from '@/assets/icons/ic_check.svg'; +export { default as ClosedIcon } from '@/assets/icons/ic_closed.svg'; +export { default as CopyIcon } from '@/assets/icons/ic_copy.svg'; +export { default as DownloadIcon } from '@/assets/icons/ic_download.svg'; +export { default as EditIcon } from '@/assets/icons/ic_edit.svg'; +export { default as InfoIcon } from '@/assets/icons/ic_info.svg'; + +// Arrow Icons +export { default as LeftArrowIcon } from '@/assets/icons/ic_leftarrow.svg'; +export { default as RightArrowIcon } from '@/assets/icons/ic_rightarrow.svg'; + +// Date Icons +export { default as DateIcon } from '@/assets/icons/ic_date.svg'; + +// Settings Icons +export { default as SettingsIcon } from '@/assets/icons/ic_settings.svg'; +export { default as SettingsArrowIcon } from '@/assets/icons/ic_settings_arrow.svg'; diff --git a/src/components/Navigation/index.stories.tsx b/src/components/Navigation/index.stories.tsx new file mode 100644 index 0000000..f085156 --- /dev/null +++ b/src/components/Navigation/index.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryObj } from '@storybook/nextjs'; +import Navigation from './index'; + +const meta = { + title: 'Components/Navigation', + component: Navigation, + parameters: { + layout: 'fullscreen', + viewport: { + defaultViewport: 'mobile1', + }, + }, + tags: ['autodocs'], + args: { + onItemClick: (id: string) => { + console.log('Navigation item clicked:', id); + }, + }, + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + defaultActiveId: 'calendar', + }, +}; + +export const ActiveCalendar: Story = { + args: { + defaultActiveId: 'calendar', + }, +}; + +export const ActiveAnalysis: Story = { + args: { + defaultActiveId: 'analysis', + }, +}; + +export const ActiveChat: Story = { + args: { + defaultActiveId: 'chat', + }, +}; + +export const ActiveCalculator: Story = { + args: { + defaultActiveId: 'calculator', + }, +}; + +export const ActiveMy: Story = { + args: { + defaultActiveId: 'my', + }, +}; diff --git a/src/components/Navigation/index.tsx b/src/components/Navigation/index.tsx new file mode 100644 index 0000000..b506e44 --- /dev/null +++ b/src/components/Navigation/index.tsx @@ -0,0 +1,113 @@ +'use client'; + +import { useState } from 'react'; +import { + CalendarIcon, + AnalysisIcon, + ChatIcon, + CalculateIcon, + MyIcon, +} from '@/components/Icons/index'; + +type NavigationItemId = 'calendar' | 'analysis' | 'chat' | 'calculator' | 'my'; + +interface NavigationItem { + id: NavigationItemId; + label: string; + icon: React.ComponentType>; +} + +const NAVIGATION_ITEMS: NavigationItem[] = [ + { id: 'calendar', label: '가계부', icon: CalendarIcon }, + { id: 'analysis', label: '분석', icon: AnalysisIcon }, + { id: 'chat', label: '챗봇', icon: ChatIcon }, + { id: 'calculator', label: '정산', icon: CalculateIcon }, + { id: 'my', label: '마이', icon: MyIcon }, +]; + +interface NavigationProps { + defaultActiveId?: NavigationItemId; + onItemClick?: (id: NavigationItemId) => void; +} + +function NavigationItemButton({ + item, + isActive, + isCenter, + onClick, +}: { + item: NavigationItem; + isActive: boolean; + isCenter: boolean; + onClick: () => void; +}) { + const baseClasses = + 'flex flex-col items-center justify-center gap-1 transition-colors cursor-pointer'; + const IconComponent = item.icon; + + if (isCenter) { + return ( + + ); + } + + const textColorClass = isActive + ? 'text-[var(--color-green-normal)]' + : 'text-[var(--color-grey-light-active)]'; + + return ( + + ); +} + +export default function Navigation({ defaultActiveId = 'calendar', onItemClick }: NavigationProps) { + const [activeId, setActiveId] = useState(defaultActiveId); + + const handleItemClick = (id: NavigationItemId) => { + setActiveId(id); + onItemClick?.(id); + }; + + return ( + + ); +} From 627050aba52a50e83335e5b9bf0d19996060d411 Mon Sep 17 00:00:00 2001 From: Lseojeong Date: Thu, 6 Nov 2025 17:40:33 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navigation/index.tsx | 97 ++++++++++++++++++----------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/src/components/Navigation/index.tsx b/src/components/Navigation/index.tsx index b506e44..0f507dc 100644 --- a/src/components/Navigation/index.tsx +++ b/src/components/Navigation/index.tsx @@ -17,6 +17,18 @@ interface NavigationItem { icon: React.ComponentType>; } +interface NavigationProps { + defaultActiveId?: NavigationItemId; + onItemClick?: (id: NavigationItemId) => void; +} + +interface NavigationItemButtonProps { + item: NavigationItem; + isActive: boolean; + isCenter: boolean; + onClick: () => void; +} + const NAVIGATION_ITEMS: NavigationItem[] = [ { id: 'calendar', label: '가계부', icon: CalendarIcon }, { id: 'analysis', label: '분석', icon: AnalysisIcon }, @@ -25,64 +37,75 @@ const NAVIGATION_ITEMS: NavigationItem[] = [ { id: 'my', label: '마이', icon: MyIcon }, ]; -interface NavigationProps { - defaultActiveId?: NavigationItemId; - onItemClick?: (id: NavigationItemId) => void; +const CENTER_BUTTON_ID: NavigationItemId = 'chat'; + +function getTextColorClass(isActive: boolean): string { + return isActive ? 'text-[var(--color-green-normal)]' : 'text-[var(--color-grey-light-active)]'; +} + +function getIconColorClass(isActive: boolean): string { + return isActive ? 'text-[var(--color-green-normal)]' : 'text-[var(--color-grey-light-active)]'; +} + +function getBaseButtonClasses(): string { + return 'flex flex-col items-center justify-center gap-1 transition-colors cursor-pointer'; +} + +function getChatButtonClasses(): string { + return 'flex h-11 w-11 items-center justify-center rounded-full bg-[var(--color-green-normal)] transition-colors hover:bg-[var(--color-green-normal-hover)] active:bg-[var(--color-green-normal-active)]'; +} + +function ChatNavigationButton({ item, onClick }: { item: NavigationItem; onClick: () => void }) { + const IconComponent = item.icon; + + return ( + + ); } -function NavigationItemButton({ +function RegularNavigationButton({ item, isActive, - isCenter, onClick, }: { item: NavigationItem; isActive: boolean; - isCenter: boolean; onClick: () => void; }) { - const baseClasses = - 'flex flex-col items-center justify-center gap-1 transition-colors cursor-pointer'; const IconComponent = item.icon; - - if (isCenter) { - return ( - - ); - } - - const textColorClass = isActive - ? 'text-[var(--color-green-normal)]' - : 'text-[var(--color-grey-light-active)]'; + const textColorClass = getTextColorClass(isActive); + const iconColorClass = getIconColorClass(isActive); return ( ); } +function NavigationItemButton({ item, isActive, isCenter, onClick }: NavigationItemButtonProps) { + if (isCenter) { + return ; + } + + return ; +} + export default function Navigation({ defaultActiveId = 'calendar', onItemClick }: NavigationProps) { const [activeId, setActiveId] = useState(defaultActiveId); @@ -91,6 +114,8 @@ export default function Navigation({ defaultActiveId = 'calendar', onItemClick } onItemClick?.(id); }; + const isCenterItem = (id: NavigationItemId): boolean => id === CENTER_BUTTON_ID; + return (