Skip to content

Commit 5ff49c2

Browse files
authored
Feat(client): 툴팁 연결 (#265)
* feat: Level 컴포넌트의 정보 버튼에 설명 추가 * feat: Level 컴포넌트에 풍선 도움말 추가 및 스타일 수정 * feat: Level 컴포넌트의 풍선 도움말 위치 조정 * feat: 사이드바 말풍선 추가 * fix: prevAcorn 상태 관리를 useRef로 변경 및 isPending 체크 추가 * feat: lottie-react 패키지 추가 * feat: 카테고리 관리 기능을 위한 useCategoryActions 훅 추가 및 Sidebar 컴포넌트에서 통합 * feat: 사이드바에 JobPin 가이드 포털 추가 및 Balloon 컴포넌트 수정 * fix: Sidebar 컴포넌트에서 isPending 변수명을 isAcornPending으로 변경 및 useCategoryActions의 Params 인터페이스 이름 수정 * fix: navigate 함수에서 newCategoryName 인코딩
1 parent 7a16378 commit 5ff49c2

File tree

8 files changed

+305
-135
lines changed

8 files changed

+305
-135
lines changed

apps/client/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"react": "^19.1.1",
1919
"react-dom": "^19.1.1",
2020
"react-error-boundary": "^6.0.0",
21-
"react-router-dom": "^7.8.2"
21+
"react-router-dom": "^7.8.2",
22+
"lottie-react": "^2.4.1"
2223
},
2324
"devDependencies": {
2425
"@eslint/js": "^9.33.0",

apps/client/src/assets/5_chippiface.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

apps/client/src/pages/level/Level.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Icon } from '@pinback/design-system/icons';
22
import { cn } from '@pinback/design-system/utils';
33
import LevelScene from '@pages/level/components/LevelScene';
4-
import TreeStatusCard from '@pages/level/components/TreeStatusCard';
54
import { getTreeLevel } from '@shared/utils/treeLevel';
65
import { TreeLevel } from '@pages/level/types/treeLevelType';
76
import { Badge } from '@pinback/design-system/ui';
87
import { useGetArcons } from '@shared/apis/queries';
98
import { lazy, Suspense } from 'react';
9+
import { Balloon } from '@shared/components/balloon/Balloon';
10+
1011
const LevelInfoCard = lazy(
1112
() => import('@pages/level/components/LevelInfoCard')
1213
);
@@ -15,8 +16,8 @@ const NextAcornTime = lazy(() => import('./components/NextAcornTime'));
1516
export default function Level() {
1617
const { data, isPending, isError } = useGetArcons();
1718

18-
if (isPending) return <div></div>;
19-
if (isError) return <div></div>;
19+
if (isPending) return <div />;
20+
if (isError) return <div />;
2021

2122
const acornCount = data.acornCount;
2223
const nextAcornTime = data.nextRemind;
@@ -27,22 +28,35 @@ export default function Level() {
2728
const defaultLevel: TreeLevel = 1;
2829
const level = isPending ? defaultLevel : (info.level as TreeLevel);
2930

31+
const balloonText =
32+
acornCount >= 7
33+
? '도토리를 모두 모았어요!'
34+
: `다음 레벨까지 저장한 북마크 ${
35+
acornCount === 0 || acornCount % 2 === 0 ? 1 : 2
36+
}개 다시 읽기`;
37+
3038
return (
3139
<div className={cn('bg-subcolor mx-auto h-dvh w-full overflow-hidden')}>
3240
<div className="relative h-full w-full overflow-hidden rounded-[1.2rem]">
3341
<LevelScene level={level} />
42+
3443
<div className="absolute inset-0">
3544
<div className="flex flex-col items-start gap-[2rem] px-[8rem] py-[5.2rem]">
3645
<div className="flex flex-row items-center gap-[0.8rem]">
3746
<h1 className="head3 text-font-black-1">치삐의 지식나무 숲</h1>
47+
3848
<div className="relative items-center">
3949
<button
4050
type="button"
41-
className="peer flex items-center justify-center p-[0.4rem]"
51+
className="peer flex items-center justify-center gap-[4px] p-[0.4rem]"
4252
aria-describedby="level-info-card"
4353
>
4454
<Icon name="ic_info" width={20} height={20} />
55+
<span className="body3-r text-font-gray-3">
56+
치삐의 지식나무 숲, 어떻게하면 성장하나요?
57+
</span>
4558
</button>
59+
4660
<div
4761
id="level-info-card"
4862
className={cn(
@@ -59,15 +73,21 @@ export default function Level() {
5973
</div>
6074
</div>
6175

62-
<Badge
63-
text="오늘 모은 도토리 개수"
64-
countNum={acornCount}
65-
isActive={true}
66-
/>
67-
<div className="flex">
68-
<TreeStatusCard acorns={acornCount} />
76+
<div className="relative inline-block">
77+
<Badge
78+
text="오늘 모은 도토리 개수"
79+
countNum={acornCount}
80+
isActive={true}
81+
/>
82+
83+
<div className="absolute left-1/2 top-full z-[3] mt-[0.8rem] -translate-x-[53px]">
84+
<Balloon variant="gray" side="top">
85+
<span className="caption1-m text-white">{balloonText}</span>
86+
</Balloon>
87+
</div>
6988
</div>
7089
</div>
90+
7191
{isLevel5 && (
7292
<Suspense fallback={null}>
7393
<NextAcornTime

apps/client/src/shared/components/balloon/Balloon.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ export function Balloon({
2626
<div className="relative inline-block">
2727
<div
2828
className={cn(
29-
'relative flex items-start gap-3 rounded-[4px] p-[1.2rem]',
29+
'relative flex w-full items-start whitespace-nowrap rounded-[5.5px] px-[1.2rem] py-[0.8rem]',
3030
variantStyle[variant]
3131
)}
3232
>
3333
<div className="flex-1">{children}</div>
3434

3535
{onClose && (
3636
<button type="button" onClick={onClose}>
37-
<Icon name="ic_close" size={16} />
37+
<Icon name="ic_close" size={20} />
3838
</button>
3939
)}
4040
</div>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { createPortal } from 'react-dom';
2+
import { Balloon } from '@shared/components/balloon/Balloon';
3+
4+
interface Props {
5+
anchorEl: HTMLElement | null;
6+
open: boolean;
7+
onClose?: () => void;
8+
}
9+
10+
export default function JobPinGuidePortal({ anchorEl, open, onClose }: Props) {
11+
if (!open || !anchorEl) return null;
12+
13+
const rect = anchorEl.getBoundingClientRect();
14+
15+
return createPortal(
16+
<div
17+
style={{
18+
position: 'fixed',
19+
left: rect.right + 12,
20+
top: rect.top + rect.height / 2,
21+
transform: 'translateY(-50%)',
22+
zIndex: 10,
23+
}}
24+
>
25+
<Balloon variant="gray" side="left" onClose={onClose}>
26+
<div className="flex flex-col gap-[0.2rem] text-white">
27+
<span className="caption1-sb">새 기능이 생겼어요</span>
28+
<span className="body4-r">같은 직무의 사람들이</span>
29+
<span className="body4-r">북마크한 아티클을 살펴봐요</span>
30+
</div>
31+
</Balloon>
32+
</div>,
33+
document.body
34+
);
35+
}

0 commit comments

Comments
 (0)