-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat/#78] 홈 컴포넌트 개발 #84
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
base: main
Are you sure you want to change the base?
Changes from all commits
0f41f69
4b1322a
acdd886
f139928
b6120ab
74cfc70
5daa3c8
15e19f1
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,86 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Image from "next/image"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Link from "next/link"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { usePathname } from "next/navigation"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const NAV_ITEMS = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href: "/", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "홈", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: "home", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href: "/schedule", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "일정", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: "schedule", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href: "/documents", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "문서", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: "documents", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href: "/profile", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "프로필", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon: "profile", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function BottomNavigation() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const pathname = usePathname(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <nav | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: "fixed", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bottom: "0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| left: "0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| right: "0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| zIndex: 50, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: "0 16px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backgroundColor: "white", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| paddingBottom: "env(safe-area-inset-bottom)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: "100%", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| padding: "4px 0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxWidth: "100%", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "space-between", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boxShadow: "0 -4px 9.2px -1px rgba(0, 0, 0, 0.03)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {NAV_ITEMS.map(item => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isActive = pathname === item.href; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Link | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key={item.href} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href={item.href} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| flexDirection: "column", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alignItems: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: "48px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: "48px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Image | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt={item.label} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width={48} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height={48} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+74
to
+79
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. 🧩 Analysis chain아이콘 파일의 존재 여부를 확인하세요. 각 네비게이션 아이템에 대해 일반 버전과 활성화 버전의 아이콘 파일이 필요합니다. 누락된 아이콘이 있을 경우 이미지 로딩 오류가 발생합니다. 다음 스크립트로 필요한 아이콘 파일들이 모두 존재하는지 확인하세요: 🏁 Script executed: #!/bin/bash
# Description: 필요한 아이콘 파일들이 public/icons 디렉토리에 존재하는지 확인
icons=("home" "schedule" "documents" "profile")
missing=0
for icon in "${icons[@]}"; do
if [ ! -f "public/icons/${icon}.png" ]; then
echo "❌ Missing: public/icons/${icon}.png"
missing=1
fi
if [ ! -f "public/icons/${icon}-active.png" ]; then
echo "❌ Missing: public/icons/${icon}-active.png"
missing=1
fi
done
if [ $missing -eq 0 ]; then
echo "✅ All required icon files exist"
else
echo "⚠️ Some icon files are missing"
exit 1
fiLength of output: 347 추가 검증이 필요합니다. BottomNavigation 컴포넌트의 항목 정의와 실제 public 디렉토리의 구조를 확인하겠습니다. 🏁 Script executed: #!/bin/bash
# Check the BottomNavigation component to see what icons are actually defined
cat apps/web/src/views/home/components/BottomNavigation.tsxLength of output: 1823 🏁 Script executed: #!/bin/bash
# Check what files exist in the public directory
find public -type f -name "*.png" 2>/dev/null | head -20Length of output: 0 🏁 Script executed: #!/bin/bash
# Check the structure of public/icons directory
ls -la public/icons/ 2>/dev/null || echo "Directory does not exist or is empty"Length of output: 36 공개 디렉토리에 필수 아이콘 파일이 모두 누락되어 있습니다.
현재 상태로는 이미지 로딩이 실패하여 하단 네비게이션이 표시되지 않습니다. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Link> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+62
to
+80
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. 접근성 속성이 누락되었습니다. 네비게이션 링크에 다음 접근성 개선사항이 필요합니다:
다음과 같이 수정하여 접근성을 개선하세요: <Link
key={item.href}
href={item.href}
+ aria-label={item.label}
+ aria-current={isActive ? "page" : undefined}
className={css({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "48px",
height: "48px",
})}
>
<Image
src={`/icons/${item.icon}${isActive ? "-active" : ""}.png`}
alt={item.label}
width={48}
height={48}
+ aria-hidden="true"
/>
</Link>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </nav> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,109 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { FloatingActionButtonProps } from "./FloatingActionButton.type"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function FloatingActionButton({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled = false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position = "fixed", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| top, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| left, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bottom = 16, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| right = 16, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actions = [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| icon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| closeIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: FloatingActionButtonProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isOpen, setIsOpen] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const style: React.CSSProperties = { position }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (top !== undefined) style.top = top; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (left !== undefined) style.left = left; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (bottom !== undefined) style.bottom = bottom; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (right !== undefined) style.right = right; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={className} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: "relative", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alignItems: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...style, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alignItems: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| borderRadius: "50%", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: "56px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: "56px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor: "pointer", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: "none", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backgroundColor: "#3A8DFF", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={disabled} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsOpen(!isOpen); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+59
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. 접근성 속성을 추가하세요. FloatingActionButton에 다음 접근성 속성이 누락되었습니다:
다음과 같이 수정하세요: <button
className={css({...})}
disabled={disabled}
type="button"
+ aria-expanded={isOpen}
+ aria-label={isOpen ? "액션 메뉴 닫기" : "액션 메뉴 열기"}
onClick={() => {
setIsOpen(!isOpen);
onClick?.();
}}
>
{isOpen ? (closeIcon ?? icon) : (openIcon ?? icon)}
</button>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: "absolute", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| flexDirection: "column", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gap: "20px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bottom: "76px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| right: "0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transition: "all 0.3s ease-in-out", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opacity: isOpen ? 1 : 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| visibility: isOpen ? "visible" : "hidden", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {actions?.map((action, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| key={index.toString()} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transform: isOpen ? "translateY(0)" : `translateY(${40 - index * 20}px)`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transition: "transform 0.3s ease-in-out", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transitionDelay: isOpen ? `${(index + 1) * 0.1}s` : "0s", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+73
to
+80
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. 배열 인덱스를 key로 사용하지 마세요.
각 action에 고유한 Option 1: FloatingActionButtonAction 타입에 id 추가
export type FloatingActionButtonAction = {
+ id: string;
icon: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
};그리고 key 사용: - {actions?.map((action, index) => (
+ {actions?.map((action) => (
<div
- key={index.toString()}
+ key={action.id}Option 2 (임시 방편): stable한 식별자가 없다면 - key={index.toString()}
+ key={`action-${index}`}단, Option 1이 권장되며 actions 배열이 동적으로 변경될 가능성이 있다면 반드시 고유 id를 사용해야 합니다. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| style={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| display: "flex", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alignItems: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| justifyContent: "center", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| borderRadius: "50%", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: "56px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: "56px", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cursor: "pointer", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| border: "none", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| backgroundColor: "#ffffff", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boxShadow: "0 0 5.6px 0 rgba(0, 0, 0, 0.04)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={action.disabled} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action.onClick?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {action.icon} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+82
to
+103
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. 액션 버튼에 접근성 레이블을 추가하세요. 각 액션 버튼에
export type FloatingActionButtonAction = {
id: string;
icon: React.ReactNode;
+ label: string; // 접근성을 위한 버튼 설명
onClick?: () => void;
disabled?: boolean;
};버튼에 aria-label 적용: <button
style={{...}}
disabled={action.disabled}
type="button"
+ aria-label={action.label}
onClick={() => {
action.onClick?.();
setIsOpen(false);
}}
>
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| export type FloatingActionButtonAction = { | ||
| icon: React.ReactNode; | ||
| onClick?: () => void; | ||
| disabled?: boolean; | ||
| }; | ||
|
|
||
| export type FloatingActionButtonProps = { | ||
| className?: string; | ||
| disabled?: boolean; | ||
| position?: "fixed" | "absolute"; | ||
| top?: React.CSSProperties["top"]; | ||
| left?: React.CSSProperties["left"]; | ||
| bottom?: React.CSSProperties["bottom"]; | ||
| right?: React.CSSProperties["right"]; | ||
| actions?: FloatingActionButtonAction[]; | ||
| icon?: React.ReactNode; | ||
| openIcon?: React.ReactNode; | ||
| closeIcon?: React.ReactNode; | ||
| onClick?: () => void; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { FloatingActionButton } from "./FloatingActionButton"; | ||
| export type { FloatingActionButtonProps } from "./FloatingActionButton.type"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| "use client"; | ||
|
|
||
| import { TAB_ITEMS, type TabsState } from "@/views/home/types"; | ||
| import Image from "next/image"; | ||
|
|
||
| type TabsProps = { | ||
| activeTab: TabsState; | ||
| setActiveTab: (tab: TabsState) => void; | ||
| }; | ||
|
Comment on lines
+6
to
+9
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. 🛠️ Refactor suggestion | 🟠 Major TabsProps 타입 중복 정의를 제거하세요.
export type TabsProps = {
activeTab: TabsState;
setActiveTab: (tab: TabsState) => void;
};그리고 이 파일에서 import하세요: -type TabsProps = {
- activeTab: TabsState;
- setActiveTab: (tab: TabsState) => void;
-};
+import type { TabsProps } from "@/views/home/types";🤖 Prompt for AI Agents |
||
|
|
||
| export function Tabs({ activeTab, setActiveTab }: TabsProps) { | ||
| return ( | ||
| <ul | ||
| style={{ | ||
| display: "flex", | ||
| flexDirection: "row", | ||
| alignItems: "center", | ||
| width: "100%", | ||
| padding: "0 20px", | ||
| backgroundColor: "white", | ||
| }} | ||
| > | ||
| <li | ||
| onClick={() => setActiveTab(TAB_ITEMS.DAILY)} | ||
| style={{ | ||
| cursor: "pointer", | ||
| flex: 1, | ||
| height: "35px", | ||
| display: "flex", | ||
| flexDirection: "row", | ||
| alignItems: "center", | ||
| justifyContent: "center", | ||
| gap: "12px", | ||
| }} | ||
| > | ||
| <Image | ||
| src={`/icons/check${activeTab === TAB_ITEMS.DAILY ? "-active" : ""}.png`} | ||
| alt={TAB_ITEMS.DAILY} | ||
| width={18} | ||
| height={18} | ||
| /> | ||
| <p | ||
| style={{ | ||
| fontSize: "14px", | ||
| lineHeight: "1.4", | ||
| letterSpacing: "-0.28px", | ||
| fontWeight: "400", | ||
| color: activeTab === TAB_ITEMS.DAILY ? "black" : "#B7C2D0", | ||
| }} | ||
| > | ||
| 일일 업무 | ||
| </p> | ||
| </li> | ||
| <li | ||
| onClick={() => setActiveTab(TAB_ITEMS.MEETING)} | ||
| style={{ | ||
| cursor: "pointer", | ||
| flex: 1, | ||
| height: "35px", | ||
| display: "flex", | ||
| flexDirection: "row", | ||
| alignItems: "center", | ||
| justifyContent: "center", | ||
| gap: "12px", | ||
| }} | ||
| > | ||
| <Image | ||
| src={`/icons/message${activeTab === TAB_ITEMS.MEETING ? "-active" : ""}.png`} | ||
| alt={TAB_ITEMS.MEETING} | ||
| width={20} | ||
| height={20} | ||
| /> | ||
| <p | ||
| style={{ | ||
| fontSize: "14px", | ||
| lineHeight: "1.4", | ||
| letterSpacing: "-0.28px", | ||
| fontWeight: "400", | ||
| color: activeTab === TAB_ITEMS.MEETING ? "black" : "#B7C2D0", | ||
| }} | ||
| > | ||
| 회의 일정 | ||
| </p> | ||
| </li> | ||
|
Comment on lines
+23
to
+84
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. 🛠️ Refactor suggestion | 🟠 Major 탭 아이템 렌더링 로직을 추출하여 코드 중복을 제거하세요. 두 탭 간에 구조가 거의 동일하여 코드 중복이 발생하고 있습니다. 배열을 순회하는 방식으로 리팩토링하는 것이 유지보수성을 향상시킵니다. 다음과 같이 리팩토링할 수 있습니다: const TAB_CONFIG = [
{
id: TAB_ITEMS.DAILY,
icon: "check",
label: "일일 업무",
iconSize: { width: 18, height: 18 },
},
{
id: TAB_ITEMS.MEETING,
icon: "message",
label: "회의 일정",
iconSize: { width: 20, height: 20 },
},
] as const;
export function Tabs({ activeTab, setActiveTab }: TabsProps) {
return (
<ul role="tablist" className={css({...})}>
{TAB_CONFIG.map((tab) => (
<li key={tab.id} role="presentation">
<button
role="tab"
aria-selected={activeTab === tab.id}
onClick={() => setActiveTab(tab.id)}
className={css({...})}
>
<Image
src={`/icons/${tab.icon}${activeTab === tab.id ? "-active" : ""}.png`}
alt={tab.label}
width={tab.iconSize.width}
height={tab.iconSize.height}
/>
<p className={css({
fontSize: "14px",
lineHeight: "1.4",
letterSpacing: "-0.28px",
fontWeight: "400",
color: activeTab === tab.id ? "black" : "#B7C2D0",
})}>
{tab.label}
</p>
</button>
</li>
))}
</ul>
);
}🤖 Prompt for AI Agents |
||
| </ul> | ||
| ); | ||
|
Comment on lines
+13
to
+86
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. 접근성 및 시맨틱 HTML 개선이 필요합니다. 현재 구현의 문제점:
다음과 같이 수정하여 접근성을 준수하세요: <ul
+ role="tablist"
className={css({
display: "flex",
flexDirection: "row",
alignItems: "center",
width: "100%",
px: "20px",
bg: "white",
})}
>
- <li
- onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
+ <li role="presentation">
+ <button
+ role="tab"
+ aria-selected={activeTab === TAB_ITEMS.DAILY}
+ onClick={() => setActiveTab(TAB_ITEMS.DAILY)}
+ onKeyDown={(e) => {
+ if (e.key === 'ArrowRight') setActiveTab(TAB_ITEMS.MEETING);
+ }}
className={css({
cursor: "pointer",
flex: 1,
height: "35px",
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: "12px",
+ border: "none",
+ background: "transparent",
})}
>
{/* 기존 내용 */}
+ </button>
- </li>
+ </li>두 번째 탭에도 동일하게 적용하되
|
||
| } | ||
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.
중첩 라우트에서 활성 상태 감지가 작동하지 않을 수 있습니다.
현재
pathname === item.href는 정확히 일치하는 경우만 활성 상태로 표시합니다. 예를 들어/schedule/detail과 같은 중첩 라우트에서는 일정 탭이 활성 상태로 표시되지 않습니다.다음과 같이 수정하여 하위 경로도 포함하도록 개선할 수 있습니다:
📝 Committable suggestion
🤖 Prompt for AI Agents