-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 06-StarRating 구현 (이경하) #26
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
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,17 @@ | ||
| button { | ||
| /* 1. 배경 및 테두리 초기화 */ | ||
| background: transparent; | ||
| border: none; | ||
|
|
||
| /* 2. 안팎 여백 및 크기 초기화 */ | ||
| padding: 0; | ||
| margin: 0; | ||
|
|
||
| /* 3. 마우스 커서 및 클릭 시 외곽선 제어 */ | ||
| cursor: pointer; | ||
| outline: none; | ||
|
|
||
| /* 4. 폰트 스타일 상속 (부모 요소의 글꼴을 그대로 따름) */ | ||
| font: inherit; | ||
| color: inherit; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| interface StarProps { | ||
| isFilled: boolean; | ||
| } | ||
|
|
||
| export default function Star({ isFilled }: StarProps) { | ||
| return isFilled ? ( | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| className="star-icon star-icon-filled" | ||
| fill="black" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth="2" | ||
| width="24" | ||
| height="24" | ||
| > | ||
| <path | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" | ||
| /> | ||
| </svg> | ||
| ) : ( | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| className="star-icon" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth="2" | ||
| width="24" | ||
| height="24" | ||
| > | ||
| <path | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" | ||
| /> | ||
| </svg> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { useState } from 'react'; | ||
| import Star from './Star'; | ||
| import './Star.css'; | ||
|
|
||
| interface StarRatingProps { | ||
| maxStar: number; | ||
| // filledStar: number; | ||
| // newStarRating: () => void; | ||
| } | ||
|
|
||
| export default function StarRating({ maxStar }: StarRatingProps) { | ||
| const [clickedStar, setClickedStar] = useState(0); | ||
| const [isHoveredStar, setIsHoveredStar] = useState<null | number>(null); | ||
|
|
||
| const starArray = Array.from({ length: maxStar }); | ||
|
|
||
| const activeRating = isHoveredStar !== null ? isHoveredStar : clickedStar; | ||
|
|
||
| const handleFilledStar = (index: number) => { | ||
| if (index === 0 && clickedStar === 1) { | ||
| setClickedStar(0); | ||
| } else { | ||
| setClickedStar(index + 1); | ||
| } | ||
| }; | ||
|
|
||
| const handleMouseEnter = (index: number) => { | ||
| setIsHoveredStar(index + 1); | ||
| }; | ||
|
|
||
| const handleMouseLeave = () => { | ||
| setIsHoveredStar(null); | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| {starArray.map((_, index) => ( | ||
| <button | ||
| key={index} | ||
| onClick={() => handleFilledStar(index)} | ||
| onMouseEnter={() => handleMouseEnter(index)} | ||
| onMouseLeave={handleMouseLeave} | ||
| > | ||
| <Star isFilled={index < activeRating} /> | ||
| </button> | ||
| ))} | ||
| </> | ||
|
Comment on lines
+36
to
+47
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. 현재는 빈태그로 별 목록만 반환하고 있어,
Member
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. 아하 .. 저는 불필요한 div를 제거해야겠다는 생각이었는데, 그럼 wrapper 레이아웃을 매번 따로 사용처에서 설정해야겠군요 .. 알려주셔서 감사합니다! |
||
| ); | ||
| } | ||
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.
isFilled값에 따라 SVG 전체를 조건부 렌더링하기보다는,className이나fill값만 조건부로 변경하면 코드 중복을 줄일 수 있을 것 같습니다.현재는 채워진 별과 빈 별의 SVG 구조가 거의 동일해서, 공통 SVG는 한 번만 작성하고 달라지는 속성만 분기해도 좋을 것 같습니다!
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.
조건부로 반복 코드를 줄이는 방법을 생각하지 못했네요!