Skip to content

Commit 60ea05c

Browse files
committed
Feature: 드롭다운 스타일 사안 반영 (#227)
1 parent 909627c commit 60ea05c

12 files changed

Lines changed: 536 additions & 404 deletions

File tree

app/(main)/explore/_components/event-card/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const EventCard = () => {
2222
치킨 받아가요!
2323
</span>
2424
<div className={styles.cardDescription}>
25-
<span>9/30까지</span>
25+
<span>10/30까지</span>
2626
</div>
2727
</div>
2828
<div className={styles.cardIconWrapper}>

app/(main)/explore/_components/select-tab-section/index.tsx

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,80 @@
1-
import TabButton from "@/app/(main)/_components/tab-button";
2-
import UpdownIcon from "@/shared/assets/icon/updown.svg";
1+
import Down from "@/shared/assets/icon/down.svg";
32
import { CAPSULE_SORT, type CapsuleSortType } from "@/shared/types/api/capsule";
43
import Dropdown from "@/shared/ui/dropdown";
54
import * as styles from "./select-tab-section.css";
5+
66
interface Props {
7-
onSelect: (value: string) => void;
8-
selectedTab: string;
7+
handleFilter: (value: string) => void;
8+
selectedFilter: string;
99
handleSort: (value: CapsuleSortType) => void;
10+
selectedSort: CapsuleSortType;
1011
}
1112

12-
const SelectTabSection = ({ onSelect, selectedTab, handleSort }: Props) => {
13-
const tabOptions = [
13+
const SelectTabSection = ({
14+
handleFilter,
15+
selectedFilter,
16+
handleSort,
17+
selectedSort = CAPSULE_SORT.LATEST,
18+
}: Props) => {
19+
const filterOptions = [
1420
{ label: "전체", value: "all" },
1521
{ label: "참여가능", value: "WRITABLE" },
1622
{ label: "오픈된 캡슐", value: "OPENED" },
1723
{ label: "마감된 캡슐", value: "WAITING_OPEN" },
1824
];
1925

26+
const sortOptions = [
27+
{ label: "최신 순", value: CAPSULE_SORT.LATEST },
28+
{ label: "오픈 임박 순", value: CAPSULE_SORT.OPEN_IMMINENT },
29+
{ label: "편지 마감 순", value: CAPSULE_SORT.WRITE_DEADLINE },
30+
];
31+
32+
const selectedFilterLabel = filterOptions.find(
33+
(option) => option.value === selectedFilter,
34+
)?.label;
35+
36+
const selectedSortLabel = sortOptions.find(
37+
(option) => option.value === selectedSort,
38+
)?.label;
39+
2040
return (
2141
<div className={styles.chipContainer}>
22-
<Dropdown className={styles.dropdown}>
23-
<Dropdown.Trigger>
24-
<UpdownIcon />
25-
</Dropdown.Trigger>
26-
<Dropdown.Content>
27-
<Dropdown.Item
28-
label="최신 순"
29-
onClick={() => {
30-
handleSort(CAPSULE_SORT.LATEST);
31-
}}
32-
/>
33-
<Dropdown.Item
34-
label="오픈 임박 순"
35-
onClick={() => handleSort(CAPSULE_SORT.OPEN_IMMINENT)}
36-
/>
37-
<Dropdown.Item
38-
label="편지 마감 순"
39-
onClick={() => handleSort(CAPSULE_SORT.WRITE_DEADLINE)}
40-
/>
41-
</Dropdown.Content>
42-
</Dropdown>
43-
<div className={styles.chipWrapper}>
44-
{tabOptions.map((option) => (
45-
<TabButton
46-
key={option.value}
47-
text={option.label}
48-
selected={selectedTab === option.value}
49-
onClick={() => onSelect(option.value)}
50-
/>
51-
))}
42+
<div className={styles.leftContainer}>
43+
<Dropdown className={styles.dropdown}>
44+
<Dropdown.Trigger className={styles.dropdownTrigger}>
45+
<span>{selectedSortLabel}</span>
46+
<Down />
47+
</Dropdown.Trigger>
48+
<Dropdown.Content className={styles.dropdownContent} align="left">
49+
{sortOptions.map((option) => (
50+
<Dropdown.Item
51+
key={option.value}
52+
label={option.label}
53+
onClick={() => handleSort(option.value)}
54+
isSelected={selectedSort === option.value}
55+
/>
56+
))}
57+
</Dropdown.Content>
58+
</Dropdown>
59+
</div>
60+
61+
<div className={styles.rightContainer}>
62+
<Dropdown className={styles.dropdown}>
63+
<Dropdown.Trigger className={styles.dropdownTrigger}>
64+
<span>{selectedFilterLabel}</span>
65+
<Down />
66+
</Dropdown.Trigger>
67+
<Dropdown.Content className={styles.dropdownContent} align="left">
68+
{filterOptions.map((option) => (
69+
<Dropdown.Item
70+
key={option.value}
71+
label={option.label}
72+
onClick={() => handleFilter(option.value)}
73+
isSelected={selectedFilter === option.value}
74+
/>
75+
))}
76+
</Dropdown.Content>
77+
</Dropdown>
5278
</div>
5379
</div>
5480
);

app/(main)/explore/_components/select-tab-section/select-tab-section.css.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { themeVars } from "@/shared/styles/base/theme.css";
2+
import { colorTheme } from "@/shared/styles/tokens/color";
13
import { screen } from "@/shared/styles/tokens/screen";
24
import { style } from "@vanilla-extract/css";
35

@@ -11,6 +13,18 @@ export const chipContainer = style({
1113
width: "100%",
1214
});
1315

16+
export const leftContainer = style({
17+
display: "flex",
18+
justifyContent: "flex-end",
19+
flex: 1,
20+
});
21+
22+
export const rightContainer = style({
23+
display: "flex",
24+
justifyContent: "flex-start",
25+
flex: 1,
26+
});
27+
1428
export const chipWrapper = style({
1529
display: "flex",
1630
gap: "0.6rem",
@@ -23,13 +37,24 @@ export const chipWrapper = style({
2337
});
2438

2539
export const dropdown = style({
26-
position: "absolute",
27-
right: "0",
28-
top: "100%",
29-
transform: "translateY(-180%)",
30-
marginRight: "1rem",
31-
...screen.md({
32-
top: "100%",
33-
transform: "translateY(-90%)",
34-
}),
40+
position: "relative",
41+
gap: "0.5rem",
42+
});
43+
44+
export const dropdownTrigger = style({
45+
display: "flex",
46+
alignItems: "center",
47+
justifyContent: "space-between",
48+
...themeVars.text.F15,
49+
gap: "0.5rem",
50+
padding: "1rem 1.5rem",
51+
minWidth: "11rem",
52+
backgroundColor: colorTheme.white[5],
53+
border: "none",
54+
borderRadius: "8px",
55+
cursor: "pointer",
56+
});
57+
58+
export const dropdownContent = style({
59+
marginTop: "1.2rem",
3560
});

app/(main)/explore/page.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import CardContainer from "./_components/card-container";
1313
import Footer from "./_components/footer";
1414

1515
const Explore = () => {
16-
const [selectedTab, setSelectedTab] = useState<string>("all");
16+
const [selectedFilter, setSelectedFilter] = useState<string>("all");
1717
const [selectedSort, setSelectedSort] = useState<CapsuleSortType>(
18-
CAPSULE_SORT.DEFAULT,
18+
CAPSULE_SORT.LATEST,
1919
);
2020

2121
const footerRef = useIntersectionObserver<HTMLDivElement>(
@@ -29,11 +29,11 @@ const Explore = () => {
2929

3030
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isPending } =
3131
useInfiniteQuery(
32-
capsuleQueryOptions.capsuleLists(selectedSort, selectedTab),
32+
capsuleQueryOptions.capsuleLists(selectedSort, selectedFilter),
3333
);
3434

35-
const handleSelect = (value: string) => {
36-
setSelectedTab(value);
35+
const handleFilter = (value: string) => {
36+
setSelectedFilter(value);
3737
};
3838

3939
const handleSort = (value: CapsuleSortType) => {
@@ -50,8 +50,9 @@ const Explore = () => {
5050
</RevealMotion>
5151
<SelectTabSection
5252
handleSort={handleSort}
53-
onSelect={handleSelect}
54-
selectedTab={selectedTab}
53+
handleFilter={handleFilter}
54+
selectedFilter={selectedFilter}
55+
selectedSort={selectedSort}
5556
/>
5657
<AddCapsuleButton />
5758
<CardContainer capsules={allCapsules} isLoading={isPending} />

app/(main)/my-capsule/_components/select-tab-section/index.tsx

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,85 @@
1-
import TabButton from "@/app/(main)/_components/tab-button";
2-
import UpdownIcon from "@/shared/assets/icon/updown.svg";
1+
import Down from "@/shared/assets/icon/down.svg";
32
import {
43
MY_CAPSULE_FILTER,
54
type MyCapsuleFilterType,
65
} from "@/shared/types/api/capsule";
76
import { CAPSULE_SORT, type CapsuleSortType } from "@/shared/types/api/capsule";
87
import Dropdown from "@/shared/ui/dropdown";
98
import * as styles from "./select-tab-section.css";
9+
1010
interface Props {
11-
onSelect: (value: MyCapsuleFilterType) => void;
12-
selectedTab: MyCapsuleFilterType;
11+
handleFilter: (value: MyCapsuleFilterType) => void;
12+
selectedFilter: MyCapsuleFilterType;
1313
handleSort: (value: CapsuleSortType) => void;
14+
selectedSort: CapsuleSortType;
1415
}
1516

16-
const SelectTabSection = ({ onSelect, selectedTab, handleSort }: Props) => {
17-
const tabOptions = [
17+
const SelectTabSection = ({
18+
handleFilter,
19+
selectedFilter,
20+
handleSort,
21+
selectedSort = CAPSULE_SORT.LATEST,
22+
}: Props) => {
23+
const filterOptions = [
1824
{ label: "전체", value: MY_CAPSULE_FILTER.ALL },
1925
{ label: "좋아요", value: MY_CAPSULE_FILTER.LIKED },
2026
{ label: "내가 만든 캡슐", value: MY_CAPSULE_FILTER.CREATED },
2127
{ label: "참여 중인 캡슐", value: MY_CAPSULE_FILTER.PARTICIPATING },
2228
];
2329

30+
const sortOptions = [
31+
{ label: "최신 순", value: CAPSULE_SORT.LATEST },
32+
{ label: "오픈 임박 순", value: CAPSULE_SORT.OPEN_IMMINENT },
33+
{ label: "편지 마감 순", value: CAPSULE_SORT.WRITE_DEADLINE },
34+
];
35+
36+
const selectedFilterLabel = filterOptions.find(
37+
(option) => option.value === selectedFilter,
38+
)?.label;
39+
40+
const selectedSortLabel = sortOptions.find(
41+
(option) => option.value === selectedSort,
42+
)?.label;
43+
2444
return (
2545
<div className={styles.chipContainer}>
26-
<div className={styles.chipWrapper}>
27-
{tabOptions.map((option) => (
28-
<TabButton
29-
key={option.value}
30-
text={option.label}
31-
selected={selectedTab === option.value}
32-
onClick={() => onSelect(option.value)}
33-
/>
34-
))}
46+
<div className={styles.leftContainer}>
47+
<Dropdown className={styles.dropdown}>
48+
<Dropdown.Trigger className={styles.dropdownTrigger}>
49+
<span>{selectedSortLabel}</span>
50+
<Down />
51+
</Dropdown.Trigger>
52+
<Dropdown.Content className={styles.dropdownContent} align="left">
53+
{sortOptions.map((option) => (
54+
<Dropdown.Item
55+
key={option.value}
56+
label={option.label}
57+
onClick={() => handleSort(option.value)}
58+
isSelected={selectedSort === option.value}
59+
/>
60+
))}
61+
</Dropdown.Content>
62+
</Dropdown>
63+
</div>
64+
65+
<div className={styles.rightContainer}>
66+
<Dropdown className={styles.dropdown}>
67+
<Dropdown.Trigger className={styles.dropdownTrigger}>
68+
<span>{selectedFilterLabel}</span>
69+
<Down />
70+
</Dropdown.Trigger>
71+
<Dropdown.Content className={styles.dropdownContent} align="left">
72+
{filterOptions.map((option) => (
73+
<Dropdown.Item
74+
key={option.value}
75+
label={option.label}
76+
onClick={() => handleFilter(option.value)}
77+
isSelected={selectedFilter === option.value}
78+
/>
79+
))}
80+
</Dropdown.Content>
81+
</Dropdown>
3582
</div>
36-
<Dropdown className={styles.dropdown}>
37-
<Dropdown.Trigger>
38-
<UpdownIcon />
39-
</Dropdown.Trigger>
40-
<Dropdown.Content>
41-
<Dropdown.Item
42-
label="최신 순"
43-
onClick={() => handleSort(CAPSULE_SORT.LATEST)}
44-
/>
45-
<Dropdown.Item
46-
label="오픈 임박 순"
47-
onClick={() => handleSort(CAPSULE_SORT.OPEN_IMMINENT)}
48-
/>
49-
<Dropdown.Item
50-
label="편지 마감 순"
51-
onClick={() => handleSort(CAPSULE_SORT.WRITE_DEADLINE)}
52-
/>
53-
</Dropdown.Content>
54-
</Dropdown>
5583
</div>
5684
);
5785
};

0 commit comments

Comments
 (0)