diff --git a/index.html b/index.html index 7d44fe3..6768024 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - + stepbookstep-fe diff --git a/src/assets/icons/clock.svg b/src/assets/icons/clock.svg index 051693a..5303556 100644 --- a/src/assets/icons/clock.svg +++ b/src/assets/icons/clock.svg @@ -1,6 +1,6 @@ - + diff --git a/src/assets/icons/home.svg b/src/assets/icons/home.svg index 958cf6f..749777d 100644 --- a/src/assets/icons/home.svg +++ b/src/assets/icons/home.svg @@ -1,6 +1,6 @@ - + diff --git a/src/assets/icons/search.svg b/src/assets/icons/search.svg index a5d2f60..13ed316 100644 --- a/src/assets/icons/search.svg +++ b/src/assets/icons/search.svg @@ -1,6 +1,6 @@ - + diff --git a/src/assets/icons/user.svg b/src/assets/icons/user.svg index 5c8ecc8..e1332b2 100644 --- a/src/assets/icons/user.svg +++ b/src/assets/icons/user.svg @@ -1,6 +1,6 @@ - + diff --git a/src/components/BottomBar/BottomBar.styles.ts b/src/components/BottomBar/BottomBar.styles.ts new file mode 100644 index 0000000..1ee4208 --- /dev/null +++ b/src/components/BottomBar/BottomBar.styles.ts @@ -0,0 +1,38 @@ +export const styles = { + // 전체 컨테이너 + container: ` + fixed bottom-0 + + left-1/2 + -translate-x-1/2 + w-full + max-w-[375px] + bg-gray-50 + border-t border-gray-100 + + flex justify-around items-center + z-50 + + pt-[4px] + + pb-[calc(4px+env(safe-area-inset-bottom))] + + min-h-[62px] + `, + + button: ` + flex flex-col items-center justify-center + w-full + h-[54px] + cursor-pointer + transition-colors duration-200 ease-in-out + `, + + icon: `w-6 h-6 mb-[2px]`, + + label: ` + text-[10px] + font-sb + leading-[16px] + `, +}; diff --git a/src/components/BottomBar/BottomBar.tsx b/src/components/BottomBar/BottomBar.tsx new file mode 100644 index 0000000..9f87a52 --- /dev/null +++ b/src/components/BottomBar/BottomBar.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react'; +import { styles } from './BottomBar.styles'; +import type { BottomBarProps, NavItem, TabId } from './BottomBar.types'; + +import { + HomeIcon, + SearchIcon, + ClockIcon, + UserIcon +} from '@/assets/icons'; + +const NAV_ITEMS: NavItem[] = [ + { id: 'home', label: '홈', icon: HomeIcon }, + { id: 'search', label: '탐색', icon: SearchIcon }, + { id: 'routine', label: '루틴', icon: ClockIcon }, + { id: 'mypage', label: '마이페이지', icon: UserIcon }, +]; + +const BottomBar = ({ defaultTab = 'home', onTabSelect }: BottomBarProps) => { + const [activeTab, setActiveTab] = useState(defaultTab); + + const handleTabClick = (id: TabId) => { + setActiveTab(id); + if (onTabSelect) { + onTabSelect(id); + } + }; + + return ( + + {item.label} + + + ); + })} + + ); +}; + +export default BottomBar; diff --git a/src/components/BottomBar/BottomBar.types.ts b/src/components/BottomBar/BottomBar.types.ts new file mode 100644 index 0000000..3f1368f --- /dev/null +++ b/src/components/BottomBar/BottomBar.types.ts @@ -0,0 +1,14 @@ +import type { FunctionComponent, SVGProps } from 'react'; + +export type TabId = 'home' | 'search' | 'routine' | 'mypage'; + +export interface NavItem { + id: TabId; + label: string; + icon: FunctionComponent>; +} + +export interface BottomBarProps { + defaultTab?: TabId; + onTabSelect?: (id: TabId) => void; +} diff --git a/src/components/TextField/TextField.types.ts b/src/components/TextField/TextField.types.ts index b75f77c..b70d499 100644 --- a/src/components/TextField/TextField.types.ts +++ b/src/components/TextField/TextField.types.ts @@ -8,4 +8,4 @@ export interface TextFieldProps extends Omit void; state?: TextFieldState; -} \ No newline at end of file +} diff --git a/src/pages/PlayGround.tsx b/src/pages/PlayGround.tsx index 18f9e2b..dd1b123 100644 --- a/src/pages/PlayGround.tsx +++ b/src/pages/PlayGround.tsx @@ -1,143 +1,54 @@ -import { useState } from "react"; -import { TextField } from "@/components/TextField/TextField"; -import type { TextFieldState } from "@/components/TextField/TextField.types"; // 타입 import 추가 +import { useState } from 'react'; +import BottomBar from '../components/BottomBar/BottomBar'; +import type { TabId } from '../components/BottomBar/BottomBar.types'; export default function PlayGround() { - // TextField용 state - const [searchValue, setSearchValue] = useState(""); - const [filledValue, setFilledValue] = useState("Sample Text"); - - // Success 및 Error 상태 테스트를 위한 State - const [successValue, setSuccessValue] = useState("Correct Input"); - const [errorValue, setErrorValue] = useState("Wrong Input"); - - // [중요] 7번 인터랙티브 테스트를 위한 State와 로직이 여기 있어야 합니다! - const [emailValue, setEmailValue] = useState(""); - - // 이메일 유효성 검사 함수 - const getEmailState = (value: string): TextFieldState => { - if (value.length === 0) return "default"; // 입력 없으면 기본 - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; // 이메일 정규식 - return emailRegex.test(value) ? "success" : "error"; + const [currentPage, setCurrentPage] = useState('home'); + + const renderPageContent = () => { + switch (currentPage) { + case 'home': + return ( +
+

홈 페이지

+ +
+ ); + case 'search': + return ( +
+

탐색 페이지

+
+ ); + case 'routine': + return ( +
+

루틴 페이지

+
+ ); + case 'mypage': + return ( +
+

마이페이지

+
+ ); + default: + return
페이지를 찾을 수 없습니다.
; + } }; - // 현재 상태 계산 - const currentEmailState = getEmailState(emailValue); - return ( -
-

TextField Component Playground

- - {/* TextField 섹션 */} -
-

TextField - 상태별 예시

- - {/* 1. Default State */} -
-

1. Default State (빈 필드)

- setSearchValue(e.target.value)} - /> -
- - {/* 2. Focus State */} -
-

2. Focus State (클릭 시)

- -
- - {/* 3. Filled State */} -
-

3. Filling State

- setFilledValue(e.target.value)} - /> -
- - {/* 4. Filled State (고정) */} -
-

4. Filled State

- -
- - {/* 5. Success State */} -
-

5. Success State

- setSuccessValue(e.target.value)} - state="success" - /> -
- - {/* 6. Error State */} -
-

6. Error State

- setErrorValue(e.target.value)} - state="error" - /> -
- - {/* 구분선 */} -
- - {/* 7. 실제 동작 테스트 (여기가 6번 div 밖으로 나와야 합니다) */} -
-

7. Interactive Test (실시간 유효성 검사)

-

- 아래 입력창에 이메일을 입력해보세요.
- - 입력 중: 빨간색 (Error)
- - 이메일 형식이 완성됨: 파란색 (Success)으로 자동 변경됩니다. -

- - setEmailValue(e.target.value)} - state={currentEmailState} - /> -
- -
+
+
+ {renderPageContent()} +
+ { + console.log(`[PlayGround] 탭 변경됨: ${tabId}`); // 콘솔 확인용 + setCurrentPage(tabId); // 화면 내용 변경 + }} + />
); -} \ No newline at end of file +}