Skip to content

Commit 0432f7e

Browse files
authored
Feat(client): card edit delete (#83)
* feat: 팝업 연결 * feat: OptionsMenuButton 연결 * feat: 수정 보달 연결
1 parent 0c42048 commit 0432f7e

File tree

6 files changed

+133
-6
lines changed

6 files changed

+133
-6
lines changed

apps/client/src/pages/myBookmark/MyBookmark.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,26 @@ import {
44
useGetBookmarkArticles,
55
useGetBookmarkUnreadArticles,
66
} from './apis/queries';
7+
import { REMIND_MOCK_DATA } from '@pages/remind/constants';
8+
import CardEditModal from '@shared/components/cardEditModal/CardEditModal';
9+
import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal';
10+
import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu';
11+
import { belowOf } from '@shared/utils/anchorPosition';
712

813
const MyBookmark = () => {
914
const [activeBadge, setActiveBadge] = useState<'all' | 'notRead'>('all');
15+
const [isEditOpen, setIsEditOpen] = useState(false);
16+
17+
const {
18+
state: menu,
19+
open: openMenu,
20+
close: closeMenu,
21+
style,
22+
containerRef,
23+
} = useAnchoredMenu((anchor) => belowOf(anchor, 8));
24+
25+
const getBookmarkTitle = (id: number | null) =>
26+
id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
1027

1128
const { data: readArticles } = useGetBookmarkArticles(1, 10);
1229
const { data: unreadArticles } = useGetBookmarkUnreadArticles(1, 10);
@@ -18,6 +35,7 @@ const MyBookmark = () => {
1835
return (
1936
<div className="flex flex-col py-[5.2rem] pl-[8rem]">
2037
<p className="head3">나의 북마크</p>
38+
2139
<div className="mt-[3rem] flex gap-[2.4rem]">
2240
<Badge
2341
text="전체보기"
@@ -44,6 +62,10 @@ const MyBookmark = () => {
4462
content={article.memo}
4563
// category={article.category.categoryName}
4664
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
65+
onClick={() => {}}
66+
onOptionsClick={(e) =>
67+
openMenu(article.articleId, e.currentTarget)
68+
}
4769
/>
4870
))}
4971

@@ -56,9 +78,43 @@ const MyBookmark = () => {
5678
content={article.memo}
5779
// category={article.}
5880
date={new Date(article.createdAt).toLocaleDateString('ko-KR')}
81+
onClick={() => {}}
82+
onOptionsClick={(e) =>
83+
openMenu(article.articleId, e.currentTarget)
84+
}
5985
/>
6086
))}
87+
88+
<OptionsMenuPortal
89+
open={menu.open}
90+
style={style ?? undefined}
91+
containerRef={containerRef}
92+
categoryId={menu.categoryId}
93+
getCategoryName={getBookmarkTitle}
94+
onEdit={() => {
95+
setIsEditOpen(true);
96+
closeMenu();
97+
}}
98+
onDelete={(id) => {
99+
console.log('delete', id);
100+
closeMenu();
101+
}}
102+
onClose={closeMenu}
103+
/>
61104
</div>
105+
106+
{isEditOpen && (
107+
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
108+
<div
109+
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
110+
onClick={() => setIsEditOpen(false)}
111+
/>
112+
<div className="absolute inset-0 flex items-center justify-center p-4">
113+
{/* 필요하면 menu.categoryId를 모달에 전달 */}
114+
<CardEditModal onClose={() => setIsEditOpen(false)} />
115+
</div>
116+
</div>
117+
)}
62118
</div>
63119
);
64120
};

apps/client/src/pages/remind/Remind.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
1-
import { Badge, Card } from '@pinback/design-system/ui';
21
import { useState } from 'react';
2+
import { Badge, Card } from '@pinback/design-system/ui';
3+
import CardEditModal from '@shared/components/cardEditModal/CardEditModal';
4+
import OptionsMenuPortal from '@shared/components/sidebar/OptionsMenuPortal';
5+
import { useAnchoredMenu } from '@shared/hooks/useAnchoredMenu';
6+
import { belowOf } from '@shared/utils/anchorPosition';
7+
import { REMIND_MOCK_DATA } from './constants';
38
import { useGetRemindArticles } from './apis/queries';
49
import { formatLocalDateTime } from '@shared/utils/formatDateTime';
510

611
const Remind = () => {
12+
const [isEditOpen, setIsEditOpen] = useState(false);
13+
14+
const {
15+
state: menu,
16+
open: openMenu,
17+
close: closeMenu,
18+
style,
19+
containerRef,
20+
} = useAnchoredMenu((anchor) => belowOf(anchor, 8));
21+
22+
const getItemTitle = (id: number | null) =>
23+
id == null ? '' : (REMIND_MOCK_DATA.find((d) => d.id === id)?.title ?? '');
724
const [activeBadge, setActiveBadge] = useState('notRead');
825
const formattedDate = formatLocalDateTime();
926

@@ -46,9 +63,41 @@ const Remind = () => {
4663
content={article.memo}
4764
timeRemaining={article.remindAt}
4865
category={article.category.categoryName}
66+
onOptionsClick={(e) =>
67+
openMenu(article.category.categoryId, e.currentTarget)
68+
}
4969
/>
5070
))}
71+
72+
<OptionsMenuPortal
73+
open={menu.open}
74+
style={style ?? undefined}
75+
containerRef={containerRef}
76+
categoryId={menu.categoryId}
77+
getCategoryName={getItemTitle}
78+
onEdit={() => {
79+
setIsEditOpen(true);
80+
closeMenu();
81+
}}
82+
onDelete={(id) => {
83+
console.log('delete', id);
84+
closeMenu();
85+
}}
86+
onClose={closeMenu}
87+
/>
5188
</div>
89+
90+
{isEditOpen && (
91+
<div className="fixed inset-0 z-[1000]" aria-modal="true" role="dialog">
92+
<div
93+
className="absolute inset-0 bg-black/60 backdrop-blur-[2px]"
94+
onClick={() => setIsEditOpen(false)}
95+
/>
96+
<div className="absolute inset-0 flex items-center justify-center p-4">
97+
<CardEditModal onClose={() => setIsEditOpen(false)} />
98+
</div>
99+
</div>
100+
)}
52101
</div>
53102
);
54103
};

apps/client/src/shared/utils/anchorPosition.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ export function rightOf(anchor: HTMLElement, gap = 8) {
22
const r = anchor.getBoundingClientRect();
33
return { top: r.top, left: r.right + gap };
44
}
5+
6+
export const belowOf = (anchor: HTMLElement, gap = 8) => {
7+
const r = anchor.getBoundingClientRect();
8+
return { top: r.bottom + gap, left: r.left };
9+
};

packages/design-system/src/components/card/Card.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ type BaseProps = {
66
content?: string;
77
category?: string;
88
imageUrl?: string;
9+
onClick?: () => void;
10+
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
911
};
1012

1113
type RemindProps = BaseProps & {
@@ -22,14 +24,22 @@ type BookmarkProps = BaseProps & {
2224

2325
export type CardProps = RemindProps | BookmarkProps;
2426

25-
const Card = (props: CardProps) => {
26-
const { type } = props;
27+
const Card = (
28+
props: CardProps & {
29+
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
30+
}
31+
) => {
32+
const { type, onOptionsClick } = props;
2733

2834
return (
2935
<>
30-
{type === 'remind' && <RemindCard {...props} />}
36+
{type === 'remind' && (
37+
<RemindCard {...props} onOptionsClick={onOptionsClick} />
38+
)}
3139

32-
{type === 'bookmark' && <MyBookmarkCard {...props} />}
40+
{type === 'bookmark' && (
41+
<MyBookmarkCard {...props} onOptionsClick={onOptionsClick} />
42+
)}
3343
</>
3444
);
3545
};

packages/design-system/src/components/card/MyBookmarkCard.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ interface MyBookmarkCardProps {
88
category?: string;
99
imageUrl?: string;
1010
date: string;
11+
onClick?: () => void;
12+
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
1113
}
1214

1315
const MyBookmarkCard = ({
@@ -16,6 +18,7 @@ const MyBookmarkCard = ({
1618
category,
1719
imageUrl,
1820
date,
21+
onOptionsClick,
1922
}: MyBookmarkCardProps) => {
2023
return (
2124
<BaseCard>
@@ -38,6 +41,7 @@ const MyBookmarkCard = ({
3841
type="button"
3942
aria-label="카테고리 상세"
4043
className="cursor-pointer self-start"
44+
onClick={(e) => onOptionsClick?.(e)}
4145
>
4246
<Icon name="ic_details_category" />
4347
</button>

packages/design-system/src/components/card/RemindCard.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ interface RemindCardProps {
88
category?: string;
99
imageUrl?: string;
1010
timeRemaining: string;
11+
onClick?: () => void;
12+
onOptionsClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
1113
}
1214

1315
const RemindCard = ({
@@ -16,6 +18,7 @@ const RemindCard = ({
1618
category,
1719
imageUrl,
1820
timeRemaining,
21+
onOptionsClick,
1922
}: RemindCardProps) => {
2023
return (
2124
<BaseCard>
@@ -46,6 +49,7 @@ const RemindCard = ({
4649
type="button"
4750
aria-label="카테고리 상세"
4851
className="cursor-pointer self-start"
52+
onClick={(e) => onOptionsClick?.(e)}
4953
>
5054
<Icon name="ic_details_category" />
5155
</button>
@@ -54,7 +58,6 @@ const RemindCard = ({
5458
{content}
5559
</p>
5660

57-
{/* TODO: 카테고리 컴포넌트로 교체 */}
5861
<span className="bg-category-red-bg caption2-sb text-category-red-text h-[2.2rem] w-[6.2rem] rounded-[0.4rem] px-[0.8rem] py-[0.2rem]">
5962
{category}
6063
</span>

0 commit comments

Comments
 (0)